package com.xbongbong.saas.service.toolbox.help;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.i18n.util.I18nMessageUtil;
import com.xbongbong.paas.constant.MessageConstant;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.constant.TimeConstant;
import com.xbongbong.paas.domain.entity.PaasAppEntity;
import com.xbongbong.paas.domain.entity.PaasFormEntity;
import com.xbongbong.paas.domain.entity.PaasFormExplainEntity;
import com.xbongbong.paas.domain.entity.ext.OptionalRangeEntity;
import com.xbongbong.paas.domain.entity.ext.PaasAttachmentEntityExt;
import com.xbongbong.paas.domain.entity.ext.PaasFormEntityExt;
import com.xbongbong.paas.enums.AggTypeEnum;
import com.xbongbong.paas.enums.ComboTypeEnum;
import com.xbongbong.paas.enums.ConditionEnum;
import com.xbongbong.paas.enums.DateTimeEnum;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.EnableEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.MenuTypeEnum;
import com.xbongbong.paas.enums.ModuleTypeEnum;
import com.xbongbong.paas.enums.OptionalRangeEnum;
import com.xbongbong.paas.enums.PatternEnum;
import com.xbongbong.paas.enums.RelatedPersonnelEnum;
import com.xbongbong.paas.enums.StageTypeEnum;
import com.xbongbong.paas.enums.StarEnum;
import com.xbongbong.paas.enums.TaskTypeEnum;
import com.xbongbong.paas.enums.UnitEnum;
import com.xbongbong.paas.enums.VisibleTypeEnum;
import com.xbongbong.paas.enums.bi.DataSetTitleEnum;
import com.xbongbong.paas.field.CustomRecheckRulePojo;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.field.ItemPoJo;
import com.xbongbong.paas.field.LocationCenterPoJo;
import com.xbongbong.paas.field.NumericalLimitsPoJo;
import com.xbongbong.paas.field.SubFormPoJo;
import com.xbongbong.paas.field.TuningPoJo;
import com.xbongbong.paas.field.VisibleRulePoJo;
import com.xbongbong.paas.field.VisibleRuleVO;
import com.xbongbong.paas.help.CommonHelp;
import com.xbongbong.paas.help.ProFormHelp;
import com.xbongbong.paas.model.PaasAppModel;
import com.xbongbong.paas.model.PaasFormExplainModel;
import com.xbongbong.paas.model.PaasFormModel;
import com.xbongbong.paas.model.PaasMenuModel;
import com.xbongbong.paas.pojo.AttrValuePojo;
import com.xbongbong.paas.pojo.ClueRecheckJointRulePojo;
import com.xbongbong.paas.pojo.FormTitlePoJo;
import com.xbongbong.paas.pojo.IdNamePropertyPojo;
import com.xbongbong.paas.pojo.RecheckRuleFromDbPojo;
import com.xbongbong.paas.pojo.StatisticOptionPojo;
import com.xbongbong.paas.pojo.SummaryDataPoJo;
import com.xbongbong.paas.pojo.dto.UserAndDepartmentGetDTO;
import com.xbongbong.paas.pojo.vo.UserAndDepartmentGetVO;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.helper.ReflectHelper;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.CompareUtil;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.paas.toolbox.util.JsonHelperUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.paas.toolbox.wrap.Entity;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.pro.constant.BusinessConstant;
import com.xbongbong.pro.constant.CommonConstant;
import com.xbongbong.pro.constant.I18nStringConstant;
import com.xbongbong.pro.constant.SymbolConstant;
import com.xbongbong.pro.contractperformance.pojo.ContractPerformanceLogPojo;
import com.xbongbong.pro.datawarning.pojo.DataWarningFieldAttrPojo;
import com.xbongbong.pro.datawarning.pojo.DwTriggerConditionPojo;
import com.xbongbong.pro.decisiontree.pojo.DecisionTreeLogDataPojo;
import com.xbongbong.pro.decisiontree.pojo.DecisionTreeLogPojo;
import com.xbongbong.pro.domain.entity.LabelEntity;
import com.xbongbong.pro.enums.WeekEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.enums.log.enums.DetailLogParamEnum;
import com.xbongbong.pro.enums.smartsettings.enums.SmartActionTypeEnum;
import com.xbongbong.pro.enums.smartsettings.enums.SmartSettingsConditionEnum;
import com.xbongbong.pro.enums.smartsettings.enums.SmartSettingsFieldEnum;
import com.xbongbong.pro.enums.smartsettings.enums.StrategyTypeEnum;
import com.xbongbong.pro.enums.teammembers.enums.CoUserPermissionEnum;
import com.xbongbong.pro.form.pojo.FormPojo;
import com.xbongbong.pro.log.pojo.DetailLogComplexPojo;
import com.xbongbong.pro.log.pojo.DetailLogParamPojo;
import com.xbongbong.pro.log.pojo.DetailLogPojo;
import com.xbongbong.pro.log.pojo.DuplicateClueLinkPojo;
import com.xbongbong.pro.performanceset.pojo.PerformanceSetExplainPojo;
import com.xbongbong.pro.pojo.DataSetLogInfoPojo;
import com.xbongbong.pro.smartsettings.pojo.AiTriggerConditionLogPojo;
import com.xbongbong.pro.smartsettings.pojo.SmartFieldAttrPojo;
import com.xbongbong.pro.smartsettings.pojo.SmartStrategyPojo;
import com.xbongbong.pro.statistic.model.ChartCategoryModel;
import com.xbongbong.pro.statistic.pojo.ChartPermissionDetailsPojo;
import com.xbongbong.pro.statistic.pojo.ChartPermissionsPojo;
import com.xbongbong.pro.workorder.pojo.WorkOrderTemplateDetailWorkflowPojo;
import com.xbongbong.pro.workreportset.pojo.WorkReportSetUserCcPojo;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ParameterConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.ContractPerformanceEntity;
import com.xbongbong.saas.domain.entity.KnowledgeCatalogueEntity;
import com.xbongbong.saas.domain.entity.KnowledgeTagEntity;
import com.xbongbong.saas.domain.entity.WorkOrderFormEntity;
import com.xbongbong.saas.enums.AssessTypeEnum;
import com.xbongbong.saas.enums.CompanyConfigEnum;
import com.xbongbong.saas.enums.MinCycleEnum;
import com.xbongbong.saas.enums.OperateModuleTypeEnum;
import com.xbongbong.saas.enums.OperateTypeEnum;
import com.xbongbong.saas.enums.PerformanceLogEnum;
import com.xbongbong.saas.enums.RedundantTemplateTypeEnum;
import com.xbongbong.saas.enums.RepeatTypeEnum;
import com.xbongbong.saas.enums.SetTypeEnum;
import com.xbongbong.saas.enums.ShowTypeEnum;
import com.xbongbong.saas.enums.SignInSetEnum;
import com.xbongbong.saas.enums.WorkOrderStageRobTypeEnum;
import com.xbongbong.saas.enums.WorkOrderStageRuleEnum;
import com.xbongbong.saas.enums.WorkReportDeadDayTypeEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.base.KnowledgeDataBaseEnum;
import com.xbongbong.saas.enums.base.PaymentSheetBaseEnum;
import com.xbongbong.saas.enums.business.AllFieldEnum;
import com.xbongbong.saas.enums.business.AssembleEnum;
import com.xbongbong.saas.enums.business.BomBillEnum;
import com.xbongbong.saas.enums.business.CommunicatePlanEnum;
import com.xbongbong.saas.enums.business.ContactEnum;
import com.xbongbong.saas.enums.business.ContractEnum;
import com.xbongbong.saas.enums.business.ContractPerformanceEnum;
import com.xbongbong.saas.enums.business.CustomerCommunicateEnum;
import com.xbongbong.saas.enums.business.CustomerManagementEnum;
import com.xbongbong.saas.enums.business.InstockEnum;
import com.xbongbong.saas.enums.business.InventoryEnum;
import com.xbongbong.saas.enums.business.InvoiceEnum;
import com.xbongbong.saas.enums.business.KnowledgeDataEnum;
import com.xbongbong.saas.enums.business.OutstockEnum;
import com.xbongbong.saas.enums.business.PayPlanEnum;
import com.xbongbong.saas.enums.business.PaySheetEnum;
import com.xbongbong.saas.enums.business.PaymentEnum;
import com.xbongbong.saas.enums.business.ProductEnum;
import com.xbongbong.saas.enums.business.ProductionOrderEnum;
import com.xbongbong.saas.enums.business.PurchaseEnum;
import com.xbongbong.saas.enums.business.PurchaseInvoiceEnum;
import com.xbongbong.saas.enums.business.QuotationEnum;
import com.xbongbong.saas.enums.business.RefundEnum;
import com.xbongbong.saas.enums.business.ReturnedPurchaseEnum;
import com.xbongbong.saas.enums.business.SalesOpportunityEnum;
import com.xbongbong.saas.enums.business.SupplierCommunicateEnum;
import com.xbongbong.saas.enums.business.SupplierContactEnum;
import com.xbongbong.saas.enums.business.SwotIndexEnum;
import com.xbongbong.saas.enums.business.TransferEnum;
import com.xbongbong.saas.enums.business.WorkOrderEnum;
import com.xbongbong.saas.enums.business.WorkReportTypeEnum;
import com.xbongbong.saas.enums.dictionary.DictionaryEnum;
import com.xbongbong.saas.model.DataDictionaryModel;
import com.xbongbong.saas.model.KnowledgeCatalogueModel;
import com.xbongbong.saas.model.KnowledgeTagModel;
import com.xbongbong.saas.model.LabelModel;
import com.xbongbong.saas.model.WorkOrderTemplateCategoryModel;
import com.xbongbong.sys.domain.entity.DepartmentEntity;
import com.xbongbong.sys.domain.entity.RoleEntity;
import com.xbongbong.sys.domain.entity.UserEntity;
import com.xbongbong.sys.model.UserModel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.StringJoiner;
import java.util.stream.Collectors;

import static com.xbongbong.pro.enums.smartsettings.enums.StrategyTypeEnum.ANNUALLY;
import static com.xbongbong.pro.enums.smartsettings.enums.StrategyTypeEnum.DAILY;
import static com.xbongbong.pro.enums.smartsettings.enums.StrategyTypeEnum.MONTHLY;
import static com.xbongbong.pro.enums.smartsettings.enums.StrategyTypeEnum.WEEKLY;

/**
 * @author zcp
 * @version v1.0
 * @date 2019/6/28 10:09
 * @since v1.0
 */
@Component
@Slf4j
public class LogHelp {

    private static final Logger LOG = LoggerFactory.getLogger(LogHelp.class);

    @Resource
    private ChartCategoryModel chartCategoryModel;
    @Resource
    private PaasMenuModel paasMenuModel;
    @Resource
    private CommonHelp commonHelp;
    @Resource
    private DataDictionaryModel dataDictionaryModel;
    @Resource
    private WorkOrderTemplateCategoryModel workOrderTemplateCategoryModel;
    @Resource
    private PaasFormModel paasFormModel;
    @Resource
    private LabelModel labelModel;
    @Resource
    private UserModel userModel;
    @Resource
    private ProFormHelp proFormHelp;
    @Resource
    private PaasFormExplainModel paasFormExplainModel;
    @Resource
    private PaasAppModel paasAppModel;
    @Resource
    private KnowledgeCatalogueModel knowledgeCatalogueModel;
    @Resource
    private KnowledgeTagModel knowledgeTagModel;


        /**
         * 处理详情日志（普通业务操作日志），目前包含：图表分类、app、menu、流程任务、form、explain、签到设置、消息推送设置
         * @param corpid 公司id
         * @param operateModuleType 操作类型枚举
         * @param oldObj 旧对象
         * @param newObj 新对象
         * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogPojo>
         * @throws XbbException
         * @author zcp
         * @date 2019/6/28 14:51
         * @since v1.0
         * @version v1.0
         */
    public List<DetailLogPojo> getDetailArr4Normal(String corpid, OperateModuleTypeEnum operateModuleType, JSONObject oldObj, JSONObject newObj) throws XbbException {
        if (oldObj == null || newObj == null) {
            return null;
        }
        List<DetailLogPojo> detailArr;
        switch (operateModuleType) {
            case CHART_CATEGORY:
                detailArr = detailLog4ChartCategory(corpid, oldObj, newObj);
                break;
            case PAAS_APP:
                detailArr = detailLog4PaasApp(oldObj, newObj);
                break;
            case PAAS_MENU:
                detailArr = detailLog4PaasMenu(corpid, oldObj, newObj);
                break;
            case PAAS_PROCESS_TASK:
                //流程的除了状态变更判断之外，当更改了表单数据时，还需要特殊处理进行判断，此处只判断状态，不判断数据变更情况
                detailArr = detailLog4PaasProcessTask(null, oldObj, newObj);
                break;
            case PAAS_FORM:
            case PAAS_FORM_WORK_ORDER:
                detailArr = detailLog4PaasForm(oldObj, newObj);
                break;
            case PAAS_FORM_EXPLAIN:
            case PAAS_FORM_EXPLAIN_WORK_ORDER:
                detailArr = detailLog4PaasFormExplain(corpid, oldObj, newObj);
                break;
            case SIGNIN:
                detailArr = detailLog4SignInSet(oldObj, newObj);
                break;
            case MESASAGE_PUSH_SET:
                detailArr = detailLog4MessagePushSet(oldObj, newObj);
                break;
            case SERVICE_REGISTRATION:
                detailArr = detailLog4ServiceRegistration(corpid, oldObj, newObj);
                break;
            case DEVELOPER:
                detailArr = detailLog4Developer(corpid, oldObj, newObj);
                break;
            case STAGE:
                detailArr = detailLog4Stage(corpid, oldObj, newObj);
                break;
            case STAGE_WORK:
                detailArr = detailLog4StageWork(corpid, oldObj, newObj);
                break;
            case STAGE_PROCESS:
                detailArr = detailLog4StageProcess(corpid, oldObj, newObj);
                break;
            case KNOWLEDGE_BASE:
                detailArr = detailLog4KnowledgeData(corpid, oldObj, newObj);
                break;
            default:
                detailArr = null;
                break;
        }
        return detailArr;
    }

    /**
     * 服务注册
     * @param corpid
     * @param oldObj
     * @param newObj
     * @return
     * @throws XbbException
     */
    public List<DetailLogPojo> detailLog4ServiceRegistration(String corpid, JSONObject oldObj, JSONObject newObj) throws XbbException {
        List<DetailLogPojo> detailArr = new ArrayList<>();
        //true 未更改，不需插入详情日志 false 有更改，需插入详情日志
        boolean flag = true;
        for (Map.Entry<String, Object> entry : oldObj.entrySet()) {
            String attr = entry.getKey();
            Object valueOld = entry.getValue();
            Object valueNew = newObj.get(attr);
            if ("administrator".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    List<String> userIdList = new ArrayList<>();
                    userIdList.add(valueNew.toString());
                    userIdList.add(valueOld.toString());


                    List<UserEntity> userList = userModel.findEntitysByUserIds(userIdList, corpid);
                    Map<String, UserEntity> userMap = new HashMap<>(userList.size());
                    userList.forEach(item->userMap.put(item.getUserId(), item));
                    log4User(detailArr, attr, I18nMessageUtil.getMessage(I18nStringConstant.ADMINISTRATOR), valueOld, valueNew, userMap);
                }
            } else if ("user".equals(attr)) {
                flag = false;
                List<String> userIdList = new ArrayList<>();
                userIdList.addAll(JSON.parseArray(valueNew.toString(), String.class));
                userIdList.addAll(JSON.parseArray(valueOld.toString(), String.class));


                List<UserEntity> userList = userModel.findEntitysByUserIds(userIdList, corpid);
                Map<String, UserEntity> userMap = new HashMap<>(userList.size());
                userList.forEach(item->userMap.put(item.getUserId(), item));
                DetailLogPojo detailLogPojo =  log4UserGroup( attr, I18nMessageUtil.getMessage(I18nStringConstant.USER), valueOld, valueNew, userMap);
                detailArr.add(detailLogPojo);
            }else if ("platformService".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(attr, I18nMessageUtil.getMessage(I18nStringConstant.PLATFORM_SERVICE), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("type".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(attr, I18nMessageUtil.getMessage(I18nStringConstant.TYPE), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("name".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(attr, I18nMessageUtil.getMessage(I18nStringConstant.SERVICE_NAME), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("url".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(attr, I18nMessageUtil.getMessage(I18nStringConstant.URL), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("signatureFlag".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(attr, I18nMessageUtil.getMessage(I18nStringConstant.SIGNATURE_FLAG), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("signature".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(attr, I18nMessageUtil.getMessage(I18nStringConstant.SIGNATURE), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            }else if ("hmacSecretFlag".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(attr, I18nMessageUtil.getMessage(I18nStringConstant.HMAC_SECRET_FLAG), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("hmacSecret".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(attr, I18nMessageUtil.getMessage(I18nStringConstant.HMAC_SECRET), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("param".equals(attr)) {
                flag = false;
                DetailLogPojo detailLogPojo = log4Normal(attr, I18nMessageUtil.getMessage(I18nStringConstant.PARAM), valueOld, valueNew);
                detailArr.add(detailLogPojo);
            } else if ("memo".equals(attr)) {
                flag = false;
                DetailLogPojo detailLogPojo = log4Normal(attr, I18nMessageUtil.getMessage(I18nStringConstant.MEMO), valueOld, valueNew);
                detailArr.add(detailLogPojo);
            } else if ("updateTime".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    detailArr = log4UpdateTime(detailArr, attr, valueOld, valueNew);
                }
            }
        }
        if (flag) {
            //无需插入详情日志
            return null;
        }
        return detailArr;
    }
    /**
     * 开发人员
     * @param corpid
     * @param oldObj
     * @param newObj
     * @return
     * @throws XbbException
     */
    public List<DetailLogPojo> detailLog4Developer(String corpid, JSONObject oldObj, JSONObject newObj) throws XbbException {
        List<DetailLogPojo> detailArr = new ArrayList<>();
        //true 未更改，不需插入详情日志 false 有更改，需插入详情日志
        boolean flag = true;
        for (Map.Entry<String, Object> entry : oldObj.entrySet()) {
            String attr = entry.getKey();
            Object valueOld = entry.getValue();
            Object valueNew = newObj.get(attr);
            if ("developer".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    List<String> userIdList = new ArrayList<>();
                    userIdList.add(valueNew.toString());
                    userIdList.add(valueOld.toString());


                    List<UserEntity> userList = userModel.findEntitysByUserIds(userIdList, corpid);
                    Map<String, UserEntity> userMap = new HashMap<>(userList.size());
                    userList.forEach(item->userMap.put(item.getUserId(), item));
                    log4User(detailArr, attr, I18nMessageUtil.getMessage(I18nStringConstant.DEVELOPER), valueOld, valueNew, userMap);
                }
            } else if ("devPermission".equals(attr)) {
                flag = false;
                DetailLogPojo detailLogPojo = log4Form(corpid, attr, I18nMessageUtil.getMessage(I18nStringConstant.DEV_PERMISSION), valueOld, valueNew);
                detailArr.add(detailLogPojo);
            }else if ("memo".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(attr, I18nMessageUtil.getMessage(I18nStringConstant.DEVELOPER_MEMO), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("updateTime".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    detailArr = log4UpdateTime(detailArr, attr, valueOld, valueNew);
                }
            }
        }
        if (flag) {
            //无需插入详情日志
            return null;
        }
        return detailArr;
    }

    /**
     * 阶段操作-详细日志封装
     * @param corpid
     * @param oldObj
     * @param newObj
     * @return
     * @throws XbbException
     */
    public List<DetailLogPojo> detailLog4Stage(String corpid, JSONObject oldObj, JSONObject newObj) throws XbbException {
        List<DetailLogPojo> detailArr = new ArrayList<>();

        if (Objects.isNull(oldObj) || oldObj.isEmpty()) {
            //新建阶段
            log4UpdateTime(detailArr, StringConstant.UPDATE_TIME, "", newObj.getLong(StringConstant.UPDATE_TIME));
            String stageName = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_NEW), newObj.getString(StringConstant.SAAS_LINK_BUSINESS_NAME));
            String msg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, stageName);
            detailArr.add(new DetailLogPojo(StringConstant.STAGE_LOG_ATTR_EN, StringConstant.STAGE_LOG_ATTR_CN, "", msg));
            return detailArr;
        }
        if (Objects.isNull(newObj) || newObj.isEmpty()) {
            //删除阶段
            log4UpdateTime(detailArr, StringConstant.UPDATE_TIME, "", oldObj.getLong(StringConstant.UPDATE_TIME));
            String stageName = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_DEL), oldObj.getString(StringConstant.SAAS_LINK_BUSINESS_NAME));
            String msg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, stageName);
            detailArr.add(new DetailLogPojo(StringConstant.STAGE_LOG_ATTR_EN, StringConstant.STAGE_LOG_ATTR_CN, "", msg));
            return detailArr;
        }
        boolean isChange = false;
        for (Map.Entry<String, Object> entry : oldObj.entrySet()) {
            String attr = entry.getKey();
            Object valueOld = entry.getValue();
            Object valueNew = newObj.get(attr);

            if ("name".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    isChange = true;
                    stageAddAttr(detailArr, I18nStringConstant.STAGE_LOG_NAME, valueOld, valueNew);
                }
            } else if ("type".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    isChange = true;
                    StageTypeEnum oldStageType = StageTypeEnum.getByType(Integer.parseInt(valueOld.toString()));
                    StageTypeEnum newStageType = StageTypeEnum.getByType(Integer.parseInt(valueNew.toString()));
                    stageAddAttr(detailArr, I18nStringConstant.STAGE_LOG_TYPE, oldStageType.getName(), newStageType.getName());
                }
            } else if ("outProportion".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    isChange = true;
                    stageAddAttr(detailArr, I18nStringConstant.STAGE_LOG_OUT_PROPORTION, valueOld, valueNew);
                }
            } else if ("enable".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    isChange = true;
                    EnableEnum oldEnableEnum = EnableEnum.getByCode(Integer.parseInt(valueOld.toString()));
                    EnableEnum newEnableEnum = EnableEnum.getByCode(Integer.parseInt(valueNew.toString()));
                    String newTitle = newEnableEnum.getMemo() +  String.format(SymbolConstant.BRACKETS_ENGLISH, newObj.get(ParameterConstant.NAME));
                    String newMsg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, newTitle);
                    String oldTitle= oldEnableEnum.getMemo() + String.format(SymbolConstant.BRACKETS_ENGLISH, oldObj.get(ParameterConstant.NAME));
                    String oldMsg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, oldTitle);
                    detailArr.add(new DetailLogPojo(StringConstant.STAGE_LOG_ATTR_EN, StringConstant.STAGE_LOG_ATTR_CN, oldMsg, newMsg));
                 }
            } else if ("executor".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    isChange = true;
                    detailLog4StageExecutor(detailArr, valueOld, valueNew);
                }
            }
        }
        if (isChange) {
            log4UpdateTime(detailArr, StringConstant.UPDATE_TIME, "", newObj.getLong(StringConstant.UPDATE_TIME));
        }
        return detailArr;
    }

    private void detailLog4StageExecutor(List<DetailLogPojo> detailArr, Object valueOld, Object valueNew) {

        List<OptionalRangeEntity> oldOptionalRangeEntities = JSONArray.parseArray(valueOld.toString(), OptionalRangeEntity.class);
        List<OptionalRangeEntity> newOptionalRangeEntities = JSONArray.parseArray(valueNew.toString(), OptionalRangeEntity.class);
        Map<String, String> oldUserMap = new HashMap<>();
        Map<String, String> oldRoleMap = new HashMap<>();
        Map<String, String> oldDeptMap = new HashMap<>();
        Map<String, String> oldDynamic = new HashMap<>();
        Map<String, String> oldManager = new HashMap<>();
        Map<String, String> newDeptMap = new HashMap<>();
        Map<String, String> newUserMap = new HashMap<>();
        Map<String, String> newRoleMap = new HashMap<>();
        Map<String, String> newDynamic = new HashMap<>();
        Map<String, String> newManager = new HashMap<>();

        for (OptionalRangeEntity item : oldOptionalRangeEntities) {
            if(Objects.equals(OptionalRangeEnum.MANAGER.getValue(), item.getProperty())) {
                oldManager.put(item.getId(), item.getName());
            } else if(Objects.equals(OptionalRangeEnum.DYNAMIC_MANAGER.getValue(), item.getProperty())) {
                oldDynamic.put(item.getId(), item.getName());
            } else if (Objects.equals(OptionalRangeEnum.USER.getValue(), item.getProperty())) {
                oldUserMap.put(item.getId(), item.getName());
            } else if (Objects.equals(OptionalRangeEnum.DEPT.getValue(), item.getProperty())) {
                oldDeptMap.put(item.getId(), item.getName());
            } else if (Objects.equals(OptionalRangeEnum.ROLE.getValue(), item.getProperty())) {
                oldRoleMap.put(item.getId(), item.getName());
            }
        }
        for (OptionalRangeEntity item : newOptionalRangeEntities) {
            if(Objects.equals(OptionalRangeEnum.MANAGER.getValue(), item.getProperty())) {
                newManager.put(item.getId(), item.getName());
            } else if(Objects.equals(OptionalRangeEnum.DYNAMIC_MANAGER.getValue(), item.getProperty())) {
                newDynamic.put(item.getId(), item.getName());
            } else if (Objects.equals(OptionalRangeEnum.USER.getValue(), item.getProperty())) {
                newUserMap.put(item.getId(), item.getName());
            } else if (Objects.equals(OptionalRangeEnum.DEPT.getValue(), item.getProperty())) {
                newDeptMap.put(item.getId(), item.getName());
            } else if (Objects.equals(OptionalRangeEnum.ROLE.getValue(), item.getProperty())) {
                newRoleMap.put(item.getId(), item.getName());
            }
        }
        detailLog4StageRole(detailArr, oldUserMap, newUserMap, I18nMessageUtil.getMessage(I18nStringConstant.STAGE_LOG_EXECUTOR_USER));
        detailLog4StageRole(detailArr, oldRoleMap, newRoleMap, I18nMessageUtil.getMessage(I18nStringConstant.STAGE_LOG_EXECUTOR_ROLE));
        detailLog4StageRole(detailArr, oldDeptMap, newDeptMap, I18nMessageUtil.getMessage(I18nStringConstant.STAGE_LOG_EXECUTOR_DEPT));
        detailLog4StageRole(detailArr, oldManager, newManager, I18nMessageUtil.getMessage(I18nStringConstant.STAGE_LOG_DYNAMIC_MANAGER));
        detailLog4StageRole(detailArr, oldDynamic, newDynamic, I18nMessageUtil.getMessage(I18nStringConstant.STAGE_LOG_DYNAMIC));
    }

    private void detailLog4StageRole(List<DetailLogPojo> detailArr, Map<String, String> oldMap, Map<String, String> newMap, String msg) {
        Set<String> addSet = newMap.keySet().stream().filter(item -> !oldMap.containsKey(item)).collect(Collectors.toSet());
        Set<String> deleteSet = oldMap.keySet().stream().filter(item -> !newMap.containsKey(item)).collect(Collectors.toSet());
        if (CollectionsUtil.isNotEmpty(addSet)) {
            for (String userId : addSet) {
                detailArr.add(new DetailLogPojo(StringConstant.STAGE_LOG_ATTR_EN, StringConstant.STAGE_LOG_ATTR_CN, "", String.format(msg, I18nMessageUtil.getMessage(I18nStringConstant.STAGE_LOG_OPERATION_ADD), newMap.get(userId))));
            }
        }
        if (CollectionsUtil.isNotEmpty(deleteSet)) {
            for (String userId : deleteSet) {
                detailArr.add(new DetailLogPojo(StringConstant.STAGE_LOG_ATTR_EN, StringConstant.STAGE_LOG_ATTR_CN, "", String.format(msg, I18nMessageUtil.getMessage(I18nStringConstant.STAGE_LOG_OPERATION_DELETE), oldMap.get(userId))));
            }
        }
    }

    /**
     * 阶段任务日志
     * @param corpid
     * @param oldObj
     * @param newObj
     * @return
     * @throws XbbException
     */
    public List<DetailLogPojo> detailLog4StageWork(String corpid, JSONObject oldObj, JSONObject newObj) throws XbbException {
        List<DetailLogPojo> detailArr = new ArrayList<>();

        if (Objects.isNull(oldObj) || oldObj.isEmpty()) {
            //新建任务
            log4UpdateTime(detailArr, StringConstant.UPDATE_TIME, "", newObj.getLong(StringConstant.UPDATE_TIME));
            String stageName = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_WORK_LOG), I18nMessageUtil.getMessage(I18nStringConstant.STAGE_LOG_OPERATION_ADD), newObj.getString(StringConstant.SAAS_LINK_BUSINESS_NAME));
            String msg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, stageName);
            detailArr.add(new DetailLogPojo(StringConstant.STAGE_LOG_ATTR_EN, StringConstant.STAGE_LOG_ATTR_CN, "", msg));
            return detailArr;
        }
        if (Objects.isNull(newObj) || newObj.isEmpty()) {
            //删除阶段
            log4UpdateTime(detailArr, StringConstant.UPDATE_TIME, "", oldObj.getLong(StringConstant.UPDATE_TIME));
            String stageName = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_WORK_LOG), I18nMessageUtil.getMessage(I18nStringConstant.STAGE_LOG_OPERATION_DELETE), newObj.getString(StringConstant.SAAS_LINK_BUSINESS_NAME));
            String msg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, stageName);
            detailArr.add(new DetailLogPojo(StringConstant.STAGE_LOG_ATTR_EN, StringConstant.STAGE_LOG_ATTR_CN, "", msg));
            return detailArr;
        }
        boolean isChange = false;
        for (Map.Entry<String, Object> entry : oldObj.entrySet()) {
            String attr = entry.getKey();
            Object valueOld = entry.getValue();
            Object valueNew = newObj.get(attr);
           if ("name".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    isChange = true;
                    stageAddAttr(detailArr, I18nStringConstant.STAGE_WORK_LOG_NAME, valueOld, valueNew);
                }
            } else if ("description".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    isChange = true;
                    stageWorkAddAttr(detailArr, I18nStringConstant.STAGE_WORK_LOG_DESCRIBE,newObj.getString(ParameterConstant.NAME), valueOld, valueNew);
                }
            } else if ("proportion".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    isChange = true;
                    stageWorkAddAttr(detailArr, I18nStringConstant.STAGE_WORK_LOG_PROPORTION,newObj.getString(ParameterConstant.NAME), valueOld, valueNew);
                }
            } else if ("required".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    isChange = true;
                    EnableEnum oldEnableEnum = EnableEnum.getByCode(Integer.parseInt(valueOld.toString()));
                    EnableEnum newEnableEnum = EnableEnum.getByCode(Integer.parseInt(valueNew.toString()));
                    String oldTitle = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_WORK_LOG_REQUIRED), oldEnableEnum.getMemo(), newObj.getString(ParameterConstant.NAME));
                    String newTitle = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_WORK_LOG_REQUIRED), newEnableEnum.getMemo(), newObj.getString(ParameterConstant.NAME));
                    String newMsg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, newTitle);
                    String oldMsg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, oldTitle);
                    detailArr.add(new DetailLogPojo(StringConstant.STAGE_LOG_ATTR_EN, StringConstant.STAGE_LOG_ATTR_CN, oldMsg, newMsg));
                }
            } else if ("executionEvent".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    isChange = true;
                    stageAddAttr(detailArr, I18nStringConstant.STAGE_LOG_STYLE_TIME, valueOld, valueNew);
                }
            } else if ("executor".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    isChange = true;
                    detailLog4StageExecutor(detailArr, valueOld, valueNew);
                }
            }
        }
        if (isChange) {
            log4UpdateTime(detailArr, StringConstant.UPDATE_TIME, "", newObj.getLong(StringConstant.UPDATE_TIME));
        }
        return detailArr;
    }

    /**
     * 阶段流程日志
     * @param corpid
     * @param oldObj
     * @param newObj
     * @return
     * @throws XbbException
     */
    public List<DetailLogPojo> detailLog4StageProcess(String corpid, JSONObject oldObj, JSONObject newObj) throws XbbException {
        List<DetailLogPojo> detailArr = new ArrayList<>();

        if (Objects.isNull(oldObj) || oldObj.isEmpty()) {
            //新建流程
            log4UpdateTime(detailArr, StringConstant.UPDATE_TIME, "", newObj.getLong(StringConstant.UPDATE_TIME));
            String stageName = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_PROCESS_LOG), I18nMessageUtil.getMessage(I18nStringConstant.STAGE_LOG_OPERATION_ADD), newObj.getString(StringConstant.SAAS_LINK_BUSINESS_NAME) + newObj.getString("versionName"));
            String msg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, stageName);
            detailArr.add(new DetailLogPojo(StringConstant.STAGE_LOG_ATTR_EN, StringConstant.STAGE_LOG_ATTR_CN, "", msg));
            return detailArr;
        }
        if (Objects.isNull(newObj) || newObj.isEmpty()) {
            //删除流程
            log4UpdateTime(detailArr, StringConstant.UPDATE_TIME, "", oldObj.getLong(StringConstant.UPDATE_TIME));
            String stageName = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_PROCESS_LOG), I18nMessageUtil.getMessage(I18nStringConstant.STAGE_LOG_OPERATION_DELETE), oldObj.getString(StringConstant.SAAS_LINK_BUSINESS_NAME) + oldObj.getString("versionName"));
            String msg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, stageName);
            detailArr.add(new DetailLogPojo(StringConstant.STAGE_LOG_ATTR_EN, StringConstant.STAGE_LOG_ATTR_CN, "", msg));
            return detailArr;
        }
        boolean isChange  = false;
        for (Map.Entry<String, Object> entry : oldObj.entrySet()) {
            String attr = entry.getKey();
            Object valueOld = entry.getValue();
            Object valueNew = newObj.get(attr);
            if ("enable".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    isChange = true;
                    EnableEnum oldEnableEnum = EnableEnum.getByCode(Integer.parseInt(valueOld.toString()));
                    EnableEnum newEnableEnum = EnableEnum.getByCode(Integer.parseInt(valueNew.toString()));
                    String oldTitle = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_PROCESS_LOG), oldEnableEnum.getMemo(), newObj.getString(ParameterConstant.NAME) + newObj.getString("versionName"));
                    String newTitle = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_PROCESS_LOG), newEnableEnum.getMemo(), newObj.getString(ParameterConstant.NAME) + newObj.getString("versionName"));
                    String newMsg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, newTitle);
                    String oldMsg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, oldTitle);
                    detailArr.add(new DetailLogPojo(StringConstant.STAGE_LOG_ATTR_EN, StringConstant.STAGE_LOG_ATTR_CN, oldMsg, newMsg));
                }
            } else if ("allowBack".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    isChange = true;
                    EnableEnum oldEnableEnum = EnableEnum.getByCode(Integer.parseInt(valueOld.toString()));
                    EnableEnum newEnableEnum = EnableEnum.getByCode(Integer.parseInt(valueNew.toString()));
                    String oldTitle = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_PROCESS_LOG_ALLOW_BACK), oldEnableEnum.getMemo());
                    String newTitle = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_PROCESS_LOG_ALLOW_BACK), newEnableEnum.getMemo());
                    String newMsg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, newTitle);
                    String oldMsg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, oldTitle);
                    detailArr.add(new DetailLogPojo(StringConstant.STAGE_LOG_ATTR_EN, StringConstant.STAGE_LOG_ATTR_CN, oldMsg, newMsg));
                }
            } else if ("showWorkCompletion".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    isChange = true;
                    EnableEnum oldEnableEnum = EnableEnum.getByCode(Integer.parseInt(valueOld.toString()));
                    EnableEnum newEnableEnum = EnableEnum.getByCode(Integer.parseInt(valueNew.toString()));
                    String oldTitle = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_PROCESS_LOG_ALLOW_BACK), oldEnableEnum.getMemo());
                    String newTitle = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_PROCESS_LOG_ALLOW_BACK), newEnableEnum.getMemo());
                    String newMsg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, newTitle);
                    String oldMsg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, oldTitle);
                    detailArr.add(new DetailLogPojo(StringConstant.STAGE_LOG_ATTR_EN, StringConstant.STAGE_LOG_ATTR_CN, oldMsg, newMsg));
                }
            } else if ("showTimeUse".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    isChange = true;
                    EnableEnum oldEnableEnum = EnableEnum.getByCode(Integer.parseInt(valueOld.toString()));
                    EnableEnum newEnableEnum = EnableEnum.getByCode(Integer.parseInt(valueNew.toString()));
                    String oldTitle = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_PROCESS_LOG_SHOW_TIME_USE), oldEnableEnum.getMemo());
                    String newTitle = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_PROCESS_LOG_SHOW_TIME_USE), newEnableEnum.getMemo());
                    String newMsg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, newTitle);
                    String oldMsg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, oldTitle);
                    detailArr.add(new DetailLogPojo(StringConstant.STAGE_LOG_ATTR_EN, StringConstant.STAGE_LOG_ATTR_CN, oldMsg, newMsg));
                }
            } else if ("showTimeStay".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    isChange = true;
                    EnableEnum oldEnableEnum = EnableEnum.getByCode(Integer.parseInt(valueOld.toString()));
                    EnableEnum newEnableEnum = EnableEnum.getByCode(Integer.parseInt(valueNew.toString()));
                    String oldTitle = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_PROCESS_LOG_SHOW_TIME_STAY), oldEnableEnum.getMemo());
                    String newTitle = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_PROCESS_LOG_SHOW_TIME_STAY), newEnableEnum.getMemo());
                    String newMsg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, newTitle);
                    String oldMsg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, oldTitle);
                    detailArr.add(new DetailLogPojo(StringConstant.STAGE_LOG_ATTR_EN, StringConstant.STAGE_LOG_ATTR_CN, oldMsg, newMsg));
                }
            } else if ("showWorkCompletion".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    isChange = true;
                    EnableEnum oldEnableEnum = EnableEnum.getByCode(Integer.parseInt(valueOld.toString()));
                    EnableEnum newEnableEnum = EnableEnum.getByCode(Integer.parseInt(valueNew.toString()));
                    String oldTitle = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_PROCESS_LOG_SHOW_WORK_COMPLETION), oldEnableEnum.getMemo());
                    String newTitle = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_PROCESS_LOG_SHOW_WORK_COMPLETION), newEnableEnum.getMemo());
                    String newMsg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, newTitle);
                    String oldMsg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, oldTitle);
                    detailArr.add(new DetailLogPojo(StringConstant.STAGE_LOG_ATTR_EN, StringConstant.STAGE_LOG_ATTR_CN, oldMsg, newMsg));
                }
            } else if ("showAverageCompletionTime".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    isChange = true;
                    EnableEnum oldEnableEnum = EnableEnum.getByCode(Integer.parseInt(valueOld.toString()));
                    EnableEnum newEnableEnum = EnableEnum.getByCode(Integer.parseInt(valueNew.toString()));
                    String oldTitle = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_PROCESS_LOG_SHOW_AVERAGE_COMPLETION_TIME), oldEnableEnum.getMemo());
                    String newTitle = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_PROCESS_LOG_SHOW_AVERAGE_COMPLETION_TIME), newEnableEnum.getMemo());
                    String newMsg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, newTitle);
                    String oldMsg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, oldTitle);
                    detailArr.add(new DetailLogPojo(StringConstant.STAGE_LOG_ATTR_EN, StringConstant.STAGE_LOG_ATTR_CN, oldMsg, newMsg));
                }
            }
        }
        if (isChange) {
            log4UpdateTime(detailArr, StringConstant.UPDATE_TIME, "", newObj.getLong(StringConstant.UPDATE_TIME));
        }
        return detailArr;
    }

    /**
     * 封装阶段日志格式
     * @param detailArr
     * @param title
     * @param valueOld
     * @param valueNew
     */
    private void stageAddAttr(List<DetailLogPojo> detailArr, String title, Object valueOld, Object valueNew) {
        String newTitle = String.format(I18nMessageUtil.getMessage(title), valueNew.toString());
        String newMsg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, newTitle);
        String oldTitle= String.format(I18nMessageUtil.getMessage(title), valueOld.toString());
        String oldMsg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, oldTitle);
        detailArr.add(new DetailLogPojo(StringConstant.STAGE_LOG_ATTR_EN, StringConstant.STAGE_LOG_ATTR_CN, oldMsg, newMsg));
    }

    /**
     * 封装阶段任务日志格式
     * @param detailArr
     * @param title
     * @param workName
     * @param valueOld
     * @param valueNew
     */
    private void stageWorkAddAttr(List<DetailLogPojo> detailArr, String title, String workName, Object valueOld, Object valueNew) {
        String newTitle = String.format(I18nMessageUtil.getMessage(title), workName, valueNew.toString());
        String newMsg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, newTitle);
        String oldTitle= String.format(I18nMessageUtil.getMessage(title), workName, valueOld.toString());
        String oldMsg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, oldTitle);
        detailArr.add(new DetailLogPojo(StringConstant.STAGE_LOG_ATTR_EN, StringConstant.STAGE_LOG_ATTR_CN, oldMsg, newMsg));
    }


    private DetailLogPojo log4Form(String corpid, String attr, String message, Object valueOld, Object valueNew) throws XbbException {
        List<FormPojo> list1 = JSON.parseArray(valueOld.toString(), FormPojo.class);
        List<FormPojo> list2 = JSON.parseArray(valueNew.toString(), FormPojo.class);
        Set<Long> workOrderFormIdList = new HashSet<>();
        Set<Long> formIdList = new HashSet<>();
        Set<String> oldId = new HashSet<>();
        Set<String> newId = new HashSet<>();
        if (CollectionsUtil.isNotEmpty(list1)) {
            list1.forEach(formPojo->{
                if (Objects.equals(XbbRefTypeEnum.WORK_ORDER.getCode(), formPojo.getBusinessType())) {
                    workOrderFormIdList.add(formPojo.getFormId());
                    oldId.add(XbbRefTypeEnum.WORK_ORDER.getAlias() + formPojo.getFormId());
                } else {
                    formIdList.add(formPojo.getFormId());
                    oldId.add(formPojo.getFormId().toString());
                }

            });
        }
        if (CollectionsUtil.isNotEmpty(list2)) {
            list2.forEach(formPojo->{
                if (Objects.equals(XbbRefTypeEnum.WORK_ORDER.getCode(), formPojo.getBusinessType())) {
                    workOrderFormIdList.add(formPojo.getFormId());
                    newId.add(XbbRefTypeEnum.WORK_ORDER.getAlias() + formPojo.getFormId());
                } else {
                    formIdList.add(formPojo.getFormId());
                    newId.add(formPojo.getFormId().toString());
                }

            });
        }
        Map<Long, PaasFormEntity> formMap = proFormHelp.getFormMap(formIdList, corpid, null);
        Map<Long, WorkOrderFormEntity> workOrderFormMap = proFormHelp.getWorkOrderFormMap(workOrderFormIdList, corpid, null);
        List<String> updateBeforeList = new ArrayList<>();
        oldId.forEach(item->{
            if (item.contains(XbbRefTypeEnum.WORK_ORDER.getAlias())) {
                updateBeforeList.add(workOrderFormMap.get(Long.valueOf(item.replace(XbbRefTypeEnum.WORK_ORDER.getAlias(), ""))).getName());
            } else {
                updateBeforeList.add(formMap.get(Long.valueOf(item)).getName());
            }
        });
        List<String> updataAfterList = new ArrayList<>();
        newId.forEach(item->{
            if (item.contains(XbbRefTypeEnum.WORK_ORDER.getAlias())) {
                updataAfterList.add(workOrderFormMap.get(Long.valueOf(item.replace(XbbRefTypeEnum.WORK_ORDER.getAlias(), ""))).getName());
            } else {
                updataAfterList.add(formMap.get(Long.valueOf(item)).getName());
            }
        });
        return new DetailLogPojo(attr, message, StringUtils.join(updateBeforeList, "，"), StringUtils.join(updataAfterList, "，"));
    }

    /**
     * 处理详情日志（特殊业务操作日志，需要封装员工、部门容器），目前包含：工单模板设置（不是工单form）
     * @param corpid 公司id
     * @param operateModuleType 操作类型枚举
     * @param oldObj 旧对象
     * @param newObj 新对象
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogPojo>
     * @throws XbbException
     * @author zcp
     * @date 2019/7/8 19:33
     * @since v1.0
     * @version v1.0
     */
    public List<DetailLogPojo> getDetailArr4Complex(String corpid, OperateModuleTypeEnum operateModuleType, JSONObject oldObj, JSONObject newObj) throws XbbException {
        if (oldObj == null || newObj == null) {
            return null;
        }
        //------封装容器detailLogDataPojo：员工、部门容器
        DetailLogParamPojo detailLogParamPojo = commonHelp.getDetailLogParamPojo4UserDepRole(corpid, DetailLogParamEnum.getEnumList4UserDep());
        List<DetailLogPojo> detailArr;
        switch (operateModuleType) {
            case WORK_ORDER:
                detailArr = detailLog4WorkOrderTemplate(corpid, detailLogParamPojo, oldObj, newObj);
                break;
            case WORKREPORT:
                detailArr = detailLog4WorkReportSet(oldObj, newObj);
                break;
            default:
                detailArr = null;
                break;
        }

        return detailArr;
    }

    /**
     * 处理流程更改操作日志（会同步监控业务数据data变更的情况）
     * @param corpid 公司id
     * @param redundantTemplateTypeEnum 表单枚举
     * @param explainMap 字段名-字段实体
     * @param oldObj 旧对象（已经塞入业务数据旧的data）
     * @param newObj 新对象（已经塞入业务数据新的data）
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogPojo>
     * @throws XbbException
     * @author zcp
     * @date 2019/7/5 20:47
     * @since v1.0
     * @version v1.0
     */
    public List<DetailLogPojo> getDetailArr4ProcessTaskAndData(String corpid, RedundantTemplateTypeEnum redundantTemplateTypeEnum, Map<String, FieldAttrEntity> explainMap, JSONObject oldObj, JSONObject newObj) throws XbbException {
        if (oldObj == null || newObj == null) {
            return null;
        }
        //------封装容器detailLogDataPojo：员工、部门容器
        DetailLogParamPojo detailLogParamPojo = commonHelp.getDetailLogParamPojo4UserDepRole(corpid, DetailLogParamEnum.getEnumList4UserDep());
        detailLogParamPojo = setExplaninDataDictionary(detailLogParamPojo, corpid, redundantTemplateTypeEnum, explainMap);
        //先判断流程
        List<DetailLogPojo> jsonArray = detailLog4PaasProcessTask(detailLogParamPojo, oldObj, newObj);

        //判断过流程，再判断业务的数据data
        if (jsonArray == null) {
            jsonArray = new ArrayList<>();
        }
        //把流程塞入的业务data数据读出来进行判断
        JSONObject dataOld = oldObj.getJSONObject(StringConstant.JSON_DATA);
        JSONObject dataNew = newObj.getJSONObject(StringConstant.JSON_DATA);
        jsonArray = log4Data(corpid, "", jsonArray, redundantTemplateTypeEnum, detailLogParamPojo, dataOld, dataNew);
        return jsonArray;
    }

    /**
     * 表单数据日志
     * @param corpid 公司id
     * @param redundantTemplateTypeEnum 表单枚举
     * @param explainMap 字段名-字段实体
     * @param oldObj 旧对象
     * @param newObj 新对象
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogPojo>
     * @throws XbbException
     * @author zcp
     * @date 2019/7/3 0:03
     * @since v1.0
     * @version v1.0
     */
    public List<DetailLogPojo> getDetailArr4Data(String corpid, RedundantTemplateTypeEnum redundantTemplateTypeEnum, Map<String, FieldAttrEntity> explainMap, JSONObject oldObj, JSONObject newObj) throws XbbException {
        if (oldObj == null || newObj == null) {
            return null;
        }
        //------封装容器detailLogDataPojo：员工、部门容器
        DetailLogParamPojo detailLogParamPojo = commonHelp.getDetailLogParamPojo4UserDepRole(corpid, DetailLogParamEnum.getEnumList4UserDep());
        detailLogParamPojo = setExplaninDataDictionary(detailLogParamPojo, corpid, redundantTemplateTypeEnum, explainMap);
        //------拼装日志
        List<DetailLogPojo> detailArr = new ArrayList<>();
        //true 未更改，不需插入详情日志 false 有更改，需插入详情日志
        boolean flag = true;
        for (Map.Entry<String, Object> entry : oldObj.entrySet()) {
            String attr = entry.getKey();
            Object valueOld = entry.getValue();
            Object valueNew = newObj.get(attr);
            if ("serialNo".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    String name = getAttrName(explainMap, attr, FieldTypeEnum.SERIALNO.getDescription());
                    DetailLogPojo detailLogPojo = log4Normal(attr, name, valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("updateTime".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    detailArr = log4UpdateTime(detailArr, attr, valueOld, valueNew);
                }
            }else if ("departmentId".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    String attrName = getAttrName(explainMap, attr, FieldTypeEnum.DEPARTMENTID.getDescription());
                    log4Dept(detailArr, attr, attrName, valueOld, valueNew, detailLogParamPojo.getDepartmentMap());
                }
            } else if ("data".equals(attr)) {
                if (chage4Json(valueOld, valueNew)) {
                    JSONObject dataOld = JsonHelperUtil.parseObject(valueOld.toString());
                    JSONObject dataNew = valueNew == null ? null : JsonHelperUtil.parseObject(valueNew.toString());
                    //处理每个字段
                    detailArr = log4Data(corpid,"", detailArr, redundantTemplateTypeEnum, detailLogParamPojo, dataOld, dataNew);
                    //是否更改，通过size判断
                    flag = detailArr.size() <= 0;
                }
            }
            //主表的ownerId不用于更新负责人的日志记录，负责人变更的日志在团队模块单独记录，主要是字段ownerId有冲突  bug49825
            /*else if ("ownerId".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    String attrName = getAttrName(explainMap, attr, FieldTypeEnum.OWNERID.getDescription());
                    detailArr = log4User(detailArr, attr, attrName, valueOld, valueNew, detailLogParamPojo.getUserMap());
                }
            }*/
        }
        if (flag) {
            //无需插入详情日志
            return null;
        }
        return detailArr;
    }

    /**
     * 图表分类详情日志处理
     * @param corpid 公司id
     * @param oldObj 旧对象
     * @param newObj 新对象
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogPojo>
     * @author zcp
     * @date 2019/6/28 14:49
     * @since v1.0
     * @version v1.0
     */
    private List<DetailLogPojo> detailLog4ChartCategory(String corpid, JSONObject oldObj, JSONObject newObj) {
        List<DetailLogPojo> detailArr = new ArrayList<>();
        //true 未更改，不需插入详情日志 false 有更改，需插入详情日志
        boolean flag = true;
        boolean parentIdFlag = false;
        String newParentId = "0";
        String oldParentId = "0";
        for (Map.Entry<String, Object> entry : oldObj.entrySet()) {
            String attr = entry.getKey();
            Object valueOld = entry.getValue();
            Object valueNew = newObj.get(attr);
            if ("parentId".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    parentIdFlag = true;
                    oldParentId = object2String(valueOld);
                    newParentId = object2String(valueNew);
                }
            } else if ("name".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(attr, I18nMessageUtil.getMessage(I18nStringConstant.GROUP_NAME), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("enable".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Enable(attr, I18nMessageUtil.getMessage(I18nStringConstant.GROUP_STATUS), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("updateTime".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    detailArr = log4UpdateTime(detailArr, attr, valueOld, valueNew);
                }
            }
        }
        //需要处理菜单分类
        if (parentIdFlag) {
            flag = false;
            // 分类id-分类名称
            Map<String, String> idNameMap = chartCategoryModel.getIdNameMap(corpid);
            String updataAfter = idNameMap.getOrDefault(newParentId, "");
            String updataBefore = idNameMap.getOrDefault(oldParentId, "");
            detailArr.add(new DetailLogPojo("parentId", I18nMessageUtil.getMessage(I18nStringConstant.PARENT_CATEGORY), updataBefore, updataAfter));
        }
        if (flag) {
            //无需插入详情日志
            return null;
        }
        return detailArr;
    }

    /**
     * 应用 详情日志处理
     * @param oldObj 旧对象
     * @param newObj 新对象
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogPojo>
     * @author zcp
     * @date 2019/6/28 15:35
     * @since v1.0
     * @version v1.0
     */
    private List<DetailLogPojo> detailLog4PaasApp(JSONObject oldObj, JSONObject newObj) {
        List<DetailLogPojo> detailArr = new ArrayList<>();
        //true 未更改，不需插入详情日志 false 有更改，需插入详情日志
        boolean flag = true;
        for (Map.Entry<String, Object> entry : oldObj.entrySet()) {
            String attr = entry.getKey();
            Object valueOld = entry.getValue();
            Object valueNew = newObj.get(attr);
            if ("name".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(attr, I18nMessageUtil.getMessage(I18nStringConstant.APPLICATION_NAME), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("icon".equals(attr) || "color".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(attr, I18nMessageUtil.getMessage(I18nStringConstant.APPLICATION_ICON), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("updateTime".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    detailArr = log4UpdateTime(detailArr, attr, valueOld, valueNew);
                }
            }
        }
        if (flag) {
            //无需插入详情日志
            return null;
        }
        return detailArr;
    }

    /**
     * 菜单详情日志处理
     * @param corpid 公司id
     * @param oldObj 旧对象
     * @param newObj 新对象
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogPojo>
     * @author zcp
     * @date 2019/6/28 20:19
     * @since v1.0
     * @version v1.0
     */
    private List<DetailLogPojo> detailLog4PaasMenu(String corpid, JSONObject oldObj, JSONObject newObj) {
        List<DetailLogPojo> detailArr = new ArrayList<>();
        //true 未更改，不需插入详情日志 false 有更改，需插入详情日志
        boolean flag = true;
        boolean parentIdFlag = false;
        String oldParentId = "0";
        String newParentId = "0";
        for (Map.Entry<String, Object> entry : oldObj.entrySet()) {
            String attr = entry.getKey();
            Object valueOld = entry.getValue();
            Object valueNew = newObj.get(attr);
            if ("parentId".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    parentIdFlag = true;
                    oldParentId = object2String(valueOld);
                    newParentId = object2String(valueNew);
                }
            } else if ("name".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(attr, I18nMessageUtil.getMessage(I18nStringConstant.MENU_NAME), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("icon".equals(attr) || "color".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(attr, I18nMessageUtil.getMessage(I18nStringConstant.ICON_STYLE), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("type".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    Integer before = object2Int(valueOld, MenuTypeEnum.FORM.getType());
                    Integer after = object2Int(valueNew, MenuTypeEnum.FORM.getType());
                    DetailLogPojo detailLogPojo = new DetailLogPojo(attr, I18nMessageUtil.getMessage(I18nStringConstant.MENU_TYPE), MenuTypeEnum.getName(before), MenuTypeEnum.getName(after));
                    detailArr.add(detailLogPojo);
                }
            } else if ("enable".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Enable(attr, I18nMessageUtil.getMessage(I18nStringConstant.MENU_STATUS), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("updateTime".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    detailArr = log4UpdateTime(detailArr, attr, valueOld, valueNew);
                }
            }
        }
        //需要处理菜单分类
        if (parentIdFlag) {
            flag = false;
            // 菜单id-菜单名称
            Map<String, String> idNameMap = paasMenuModel.getIdNameMap(corpid);
            String updataBefore = idNameMap.getOrDefault(oldParentId, "");
            String updataAfter = idNameMap.getOrDefault(newParentId, "");
            DetailLogPojo detailLogPojo = new DetailLogPojo("parentId", I18nMessageUtil.getMessage(I18nStringConstant.MENU_GROUP), updataBefore, updataAfter);
            detailArr.add(detailLogPojo);
        }
        if (flag) {
            //无需插入详情日志
            return null;
        }
        return detailArr;
    }

    /**
     * 更新流程任务 生成操作详细日志
     * @param detailLogParamPojo 处理日志详情时，渲染模板数据需要的参数pojo
     * @param oldObj 旧对象
     * @param newObj 新对象
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogPojo>
     * @author zcp
     * @date 2019/6/28 22:05
     * @since v1.0
     * @version v1.0
     */
    private List<DetailLogPojo> detailLog4PaasProcessTask(DetailLogParamPojo detailLogParamPojo, JSONObject oldObj, JSONObject newObj) {
        List<DetailLogPojo> detailArr = new ArrayList<>();
        //true 未更改，不需插入详情日志 false 有更改，需插入详情日志
        boolean flag = true;
        for (Map.Entry<String, Object> entry : oldObj.entrySet()) {
            String attr = entry.getKey();
            Object valueOld = entry.getValue();
            Object valueNew = newObj.get(attr);
            if ("taskType".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    Integer before = object2Int(valueOld, TaskTypeEnum.UNTREATED.getType());
                    Integer after = object2Int(valueNew, TaskTypeEnum.UNTREATED.getType());
                    DetailLogPojo detailLogPojo = new DetailLogPojo(attr, I18nMessageUtil.getMessage(I18nStringConstant.APPROVAL_STATUS), TaskTypeEnum.getStatusMemo(before), TaskTypeEnum.getStatusMemo(after));
                    detailArr.add(detailLogPojo);
                }
            } else if ("opinion".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(attr, I18nMessageUtil.getMessage(I18nStringConstant.APPROVAL_COMMENTS), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("transferUserId".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    if (detailLogParamPojo != null) {
                        detailArr = log4User(detailArr, attr, I18nMessageUtil.getMessage(I18nStringConstant.TRANSFER_PRINCIPAL), valueOld, valueNew, detailLogParamPojo.getUserMap());
                    } else {
                        DetailLogPojo detailLogPojo = log4Normal(attr, I18nMessageUtil.getMessage(I18nStringConstant.TRANSFER_PRINCIPAL), valueOld, valueNew);
                        detailArr.add(detailLogPojo);
                    }
                }
            } else if ("revokeUserId".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    if (detailLogParamPojo != null) {
                        detailArr = log4User(detailArr, attr, I18nMessageUtil.getMessage(I18nStringConstant.WITHDRAW_OPERATOR), valueOld, valueNew, detailLogParamPojo.getUserMap());
                    } else {
                        DetailLogPojo detailLogPojo = log4Normal(attr, I18nMessageUtil.getMessage(I18nStringConstant.WITHDRAW_OPERATOR), valueOld, valueNew);
                        detailArr.add(detailLogPojo);
                    }
                }
            } else if ("updateTime".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    detailArr = log4UpdateTime(detailArr, attr, valueOld, valueNew);
                }
            }
        }
        if (flag) {
            //无需插入详情日志
            return null;
        }
        return detailArr;
    }

    /**
     * 表单、工单模板字段日志
     * @param oldObj 旧对象
     * @param newObj 新对象
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogPojo>
     * @author zcp
     * @date 2019/6/29 19:54
     * @since v1.0
     * @version v1.0
     */
    private List<DetailLogPojo> detailLog4PaasForm(JSONObject oldObj, JSONObject newObj) {
        List<DetailLogPojo> detailArr = new ArrayList<>();
        //true 未更改，不需插入详情日志 false 有更改，需插入详情日志
        boolean flag = true;
        for (Map.Entry<String, Object> entry : oldObj.entrySet()) {
            String attr = entry.getKey();
            Object valueOld = entry.getValue();
            Object valueNew = newObj.get(attr);
            // TODO: 2019/6/29 关联列表设置、业务规则、协同人权限
            if ("isProcessForm".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Enable(attr, ModuleTypeEnum.PROCESS_FORM.getMemo(), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("customTitle".equals(attr)) {
                if (chage4Array(valueOld, valueNew)) {
                    flag = false;
                    String updataBefore = info4FormTitlePojo(valueOld);
                    String updataAfter = info4FormTitlePojo(valueNew);
                    detailArr.add(new DetailLogPojo(attr, I18nMessageUtil.getMessage(I18nStringConstant.DATA_TITLE), updataBefore, updataAfter));
                }
            } else if ("summary".equals(attr) || "labels".equals(attr)) {
                if (chage4Array(valueOld, valueNew)) {
                    flag = false;
                    String updataBefore = info4SummaryDataPojo(valueOld);
                    String updataAfter = info4SummaryDataPojo(valueNew);
                    String attrName = "summary".equals(attr)? I18nMessageUtil.getMessage(I18nStringConstant.DATA_SUMMARY): I18nMessageUtil.getMessage(I18nStringConstant.DATA_LABEL);
                    detailArr.add(new DetailLogPojo(attr, attrName, updataBefore, updataAfter));
                }
            } else if ("updateTime".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
//                    flag = false;
                    detailArr = log4UpdateTime(detailArr, attr, valueOld, valueNew);
                }
            }
        }
        if (flag) {
            //无需插入详情日志
            return null;
        }
        return detailArr;
    }

    /**
     * 结果展示设置，更新配置
     * @param corpid
     * @param oldObj
     * @param newObj
     * @return
     * @throws XbbException
     */
    public List<DetailLogPojo> log4ResultShow(String corpid, String oldObj, String newObj, Long formId, Long oldUpdateTime) throws XbbException {
        if (StringUtil.isEmpty(oldObj) || StringUtil.isEmpty(newObj)){
            return null;
        }
        List<DetailLogPojo> detailArr = new ArrayList<>();
        //true 未更改，不需插入详情日志 false 有更改，需插入详情日志
        boolean flag = true;
        PaasFormExplainEntity paasFormExplainEntity = paasFormExplainModel.getByFormId(formId, corpid);
        if(Objects.isNull(paasFormExplainEntity)){
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_EXPLAIN_NOT_EXIST);
        }
        Map<String, FieldAttrEntity> explainMap = ExplainUtil.getExplainMap(paasFormExplainEntity.getExplains(), null);
        JSONArray oldJsonArray = JSONArray.parseArray(oldObj);
        JSONArray newJsonArray = JSONArray.parseArray(newObj);
        //比较规则是否有变化
        boolean ruleFlag = CompareUtil.compareArray(oldJsonArray, newJsonArray);
        List<String> oldValues = new ArrayList<>();
        List<String> newValues = new ArrayList<>();
        if (!ruleFlag){
            flag = false;
            //代表规则改变
            Iterator<Object> oldIterator = oldJsonArray.stream().iterator();
            while(oldIterator.hasNext()){
                FieldAttrEntity fieldAttrEntity = explainMap.get(oldIterator.next());
                if (Objects.nonNull(fieldAttrEntity)){
                    oldValues.add(fieldAttrEntity.getAttrName());
                }
            }

            Iterator<Object> newIterator = newJsonArray.stream().iterator();
            while(newIterator.hasNext()){
                FieldAttrEntity fieldAttrEntity = explainMap.get(newIterator.next());
                if (Objects.nonNull(fieldAttrEntity)){
                    newValues.add(fieldAttrEntity.getAttrName());
                }
            }
            String updataBefore = JSONObject.toJSONString(oldValues);
            String updataAfter = JSONObject.toJSONString(newValues);
            detailArr.add(new DetailLogPojo(null, OperateModuleTypeEnum.DUPLICATE_RESULT_SHOW.getName(), updataBefore, updataAfter));
        }
        //规则没有改变只添加更新时间
        long newUpdateTime = DateTimeUtil.getLong()/1000;
        detailArr = log4UpdateTime(detailArr, null, oldUpdateTime, newUpdateTime);
        if (flag) {
            //无需插入详情日志
            return null;
        }
        return detailArr;
    }

    /**
     * 线索联合客户和联系人进行查重，日志记录
     * @return
     * @throws XbbException
     */
    public List<DetailLogPojo> log4ClueLink(DuplicateClueLinkPojo duplicateClueLinkPojo, List<DetailLogPojo> detailArr) throws XbbException{
        String corpid = duplicateClueLinkPojo.getCorpid();
        ClueRecheckJointRulePojo newObj = duplicateClueLinkPojo.getNewObj();
        List<RecheckRuleFromDbPojo> newObjList = newObj.getRuleList();
        List<RecheckRuleFromDbPojo> oldObjList = new ArrayList<>();
        JSONArray oldJsonArray = null;
        if (Objects.nonNull(duplicateClueLinkPojo.getOldRules())){
            oldJsonArray = JSONArray.parseArray(duplicateClueLinkPojo.getOldRules());
            oldObjList = oldJsonArray.toJavaList(RecheckRuleFromDbPojo.class);
        }
        JSONArray newJsonArray = JSONArray.parseArray(JSONObject.toJSONString(newObjList));
        Long clueFormId = duplicateClueLinkPojo.getFormId();
        String operation = duplicateClueLinkPojo.getOperation();
        Integer linkBusinessType = duplicateClueLinkPojo.getLinkBusinessType();
//        List<DetailLogPojo> detailArr = new ArrayList<>();
        //true 未更改，不需插入详情日志 false 有更改，需插入详情日志
        boolean flag = true;
        PaasFormExplainEntity cluePaasFormExplainEntity = paasFormExplainModel.getByFormId(clueFormId, corpid);
        if(Objects.isNull(cluePaasFormExplainEntity)){
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_EXPLAIN_NOT_EXIST);
        }
        Map<String, FieldAttrEntity> clueExplainMap = getFormExplain(clueFormId, corpid);
        List<String> oldCustomerValues = new ArrayList<>();
        List<String> newCustomerValues = new ArrayList<>();

        Integer oldEnable = duplicateClueLinkPojo.getOldEnable();
        Integer newEnable = newObj.getEnable();
        boolean customerNewOpenFlag = Objects.equals(operation, OperateTypeEnum.NEW.getAlias()) &&
                Objects.equals(BasicConstant.ONE, newEnable);
        //客户新建，并且新的规则处于开启状态
        if (customerNewOpenFlag){
            if (CollectionsUtil.isNotEmpty(newObjList)){
                flag = false;
                Map<String, List<String>> resultMap = closeToOpenDeal(newObjList, corpid, newCustomerValues, clueExplainMap, linkBusinessType);
                oldCustomerValues.add("关闭");
                List<String> newCustomerValues1 = resultMap.get("customerValues");
                String updataBefore = JSONObject.toJSONString(oldCustomerValues);
                String updataAfter = JSONObject.toJSONString(newCustomerValues1);
                if (Objects.equals(XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), linkBusinessType)){
                    detailArr.add(new DetailLogPojo(null, OperateModuleTypeEnum.DUPLICATE_CLUE_JOINT_CUSTOMER.getName(), updataBefore, updataAfter));
                }else {
                    detailArr.add(new DetailLogPojo(null, OperateModuleTypeEnum.DUPLICATE_CLUE_JOINT_CONTACT.getName(), updataBefore, updataAfter));
                }

            }
        }else if (Objects.equals(operation, OperateTypeEnum.EDIT.getAlias())){
            //先判断开启状态是否改变
            boolean enableFlag = Objects.equals(BasicConstant.ZERO, oldEnable) && Objects.equals(BasicConstant.ZERO, newEnable);
            boolean newEnableOpenFlag = Objects.equals(BasicConstant.ZERO, oldEnable) && Objects.equals(BasicConstant.ONE, newEnable);
            boolean oldEnableOpenFlag = Objects.equals(BasicConstant.ONE, oldEnable) && Objects.equals(BasicConstant.ZERO, newEnable);
            boolean newAndOldAllOpenFlag = Objects.equals(BasicConstant.ONE, oldEnable) && Objects.equals(BasicConstant.ONE, newEnable);
            if (newEnableOpenFlag){
                //新的规则开启，之前处于关闭状态
                if (CollectionsUtil.isNotEmpty(newObjList)){
                    flag = false;
                    Map<String, List<String>> resultMap = closeToOpenDeal(newObjList, corpid, newCustomerValues, clueExplainMap, linkBusinessType);
                    oldCustomerValues.add("关闭");
                    List<String> newCustomerValues1 = resultMap.get("customerValues");
                    String updataBefore = JSONObject.toJSONString(oldCustomerValues);
                    String updataAfter = JSONObject.toJSONString(newCustomerValues1);
                    if (Objects.equals(XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), linkBusinessType)){
                        detailArr.add(new DetailLogPojo(null, OperateModuleTypeEnum.DUPLICATE_CLUE_JOINT_CUSTOMER.getName(), updataBefore, updataAfter));
                    }else {
                        detailArr.add(new DetailLogPojo(null, OperateModuleTypeEnum.DUPLICATE_CLUE_JOINT_CONTACT.getName(), updataBefore, updataAfter));
                    }
                }
            }else if (oldEnableOpenFlag){
                //新的规则关闭，之前处于开启状态
                if (CollectionsUtil.isNotEmpty(oldObjList)){
                    flag = false;
                    Map<String, List<String>> resultMap = closeToOpenDeal(oldObjList, corpid, oldCustomerValues, clueExplainMap, linkBusinessType);
                    newCustomerValues.add("关闭");
                    List<String> customerValues = resultMap.get("customerValues");
                    String updataBefore = JSONObject.toJSONString(customerValues);
                    String updataAfter = JSONObject.toJSONString(newCustomerValues);
                    if (Objects.equals(XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), linkBusinessType)){
                        detailArr.add(new DetailLogPojo(null, OperateModuleTypeEnum.DUPLICATE_CLUE_JOINT_CUSTOMER.getName(), updataBefore, updataAfter));
                    }else {
                        detailArr.add(new DetailLogPojo(null, OperateModuleTypeEnum.DUPLICATE_CLUE_JOINT_CONTACT.getName(), updataBefore, updataAfter));
                    }
                }
            }else if (newAndOldAllOpenFlag){
                if (CollectionsUtil.isNotEmpty(oldObjList) && CollectionsUtil.isNotEmpty(newObjList)){
                    //新的规则和旧的都处于开启状态
                    //规则改变
                    flag = false;
                    //老数据
                    Map<String, List<String>> oldResultMap = closeToOpenDeal(oldObjList, corpid, oldCustomerValues, clueExplainMap, linkBusinessType);
                    List<String> customerValuesOld = oldResultMap.get("customerValues");
                    //新数据
                    Map<String, List<String>> newResultMap = closeToOpenDeal(newObjList, corpid, newCustomerValues, clueExplainMap, linkBusinessType);
                    List<String> customerValuesNew = newResultMap.get("customerValues");
                    String updataBefore = JSONObject.toJSONString(customerValuesOld);
                    String updataAfter = JSONObject.toJSONString(customerValuesNew);
                    if (Objects.equals(XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), linkBusinessType)){
                        detailArr.add(new DetailLogPojo(null, OperateModuleTypeEnum.DUPLICATE_CLUE_JOINT_CUSTOMER.getName(), updataBefore, updataAfter));
                    }else {
                        detailArr.add(new DetailLogPojo(null, OperateModuleTypeEnum.DUPLICATE_CLUE_JOINT_CONTACT.getName(), updataBefore, updataAfter));
                    }
//                    boolean ruleFlag = CompareUtil.compareArray(oldJsonArray, newJsonArray);
//                    if (!ruleFlag){
//
//                    }
                }
            }
        }

        //区分是联合客户还是联系人
//        if (Objects.equals(XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), linkBusinessType)){
//
//        }else if (Objects.equals(XbbRefTypeEnum.CONTACT.getCode(), linkBusinessType)){
//            if (Objects.equals(operation, OperateTypeEnum.NEW.getAlias())){
//                //新建操作
//
//
//            }else if (Objects.equals(operation, OperateTypeEnum.EDIT.getAlias())){
//
//            }
//        }
        //规则没有改变只添加更新时间
//        long newUpdateTime = DateTimeUtil.getLong()/1000;
//        detailArr = log4UpdateTime(detailArr, null, duplicateClueLinkPojo.getOldUpdateTime(), newUpdateTime);
//        if (flag) {
//            //无需插入详情日志
//            return null;
//        }
        return detailArr;
    }


    /**
     * 规则由关闭状态  变为开启状态处理
     * @param newObjList
     * @return
     */
    private Map<String, List<String>> closeToOpenDeal(List<RecheckRuleFromDbPojo> newObjList, String corpid, List<String> customerValues, Map<String, FieldAttrEntity> clueExplainMap, Integer linkBusinessType) throws XbbException{
        Map<String, List<String>> resultMap = new HashMap<>();
        if (Objects.equals(XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), linkBusinessType)){
            if (CollectionsUtil.isNotEmpty(newObjList)){
                for (RecheckRuleFromDbPojo recheckRuleFromDbPojo : newObjList){
                    Integer isAll = recheckRuleFromDbPojo.getIsAll();
                    String linkRuleString = null;
                    if(Objects.equals(BasicConstant.ZERO, isAll)) {
                        //单个模板
                        String attr = recheckRuleFromDbPojo.getAttr();
                        String linkAttr = recheckRuleFromDbPojo.getLinkAttr();
                        Long customerFormId = recheckRuleFromDbPojo.getFormId();
                        Map<String, FieldAttrEntity> customerExplainMap = getFormExplain(customerFormId, corpid);
                        PaasFormEntity customerPaasForm = paasFormModel.getByFormId(customerFormId, corpid);
                        //判断attr和linkAttr是否是子表单
                        String attrValue = attrDeal(customerValues, attr, clueExplainMap);
                        String linkAttrValue = attrDeal(customerValues, linkAttr, customerExplainMap);
                        linkRuleString = attrValue + "等于" + customerPaasForm.getName() + linkAttrValue;
                        customerValues.add(linkRuleString);
                    }else if (Objects.equals(BasicConstant.ONE, isAll)) {
                        //全部模板的解释合集
                        HashMap<String, FieldAttrEntity> customerAllExplainMap = getCustomerAllTemplate(corpid, linkBusinessType);
                        String attr = recheckRuleFromDbPojo.getAttr();
                        String linkAttr = recheckRuleFromDbPojo.getLinkAttr();
                        Long customerFormId = recheckRuleFromDbPojo.getFormId();
                        //判断attr和linkAttr是否是子表单
                        String attrValue = attrDeal(customerValues, attr, clueExplainMap);
                        String linkAttrValue = attrDeal(customerValues, linkAttr, customerAllExplainMap);
                        linkRuleString = attrValue + "等于全部模板" + linkAttrValue;
                        customerValues.add(linkRuleString);
                    }
                }
            }
        }else if (Objects.equals(XbbRefTypeEnum.CONTACT.getCode(), linkBusinessType)){
            if (CollectionsUtil.isNotEmpty(newObjList)){
                for (RecheckRuleFromDbPojo recheckRuleFromDbPojo : newObjList){
                    String linkRuleString = null;
                    String attr = recheckRuleFromDbPojo.getAttr();
                    String linkAttr = recheckRuleFromDbPojo.getLinkAttr();
                    Long contactrFormId = recheckRuleFromDbPojo.getFormId();
                    Map<String, FieldAttrEntity> contactExplainMap = getFormExplain(contactrFormId, corpid);
                    PaasFormEntity contactPaasForm = paasFormModel.getByFormId(contactrFormId, corpid);
                    //判断attr和linkAttr是否是子表单
                    String attrValue = attrDeal(customerValues, attr, clueExplainMap);
                    String linkAttrValue = attrDeal(customerValues, linkAttr, contactExplainMap);
                    linkRuleString = attrValue + "等于" + contactPaasForm.getName() + linkAttrValue;
                    customerValues.add(linkRuleString);
                }
            }

        }

        resultMap.put("customerValues", customerValues);
        return resultMap;
//        oldCustomerValues.add("关闭");
//        String updataBefore = JSONObject.toJSONString(oldCustomerValues);
//        String updataAfter = JSONObject.toJSONString(newCustomerValues);
//        detailArr.add(new DetailLogPojo(null, OperateModuleTypeEnum.DUPLICATE_CLUE_JOINT_CUSTOMER.getName(), updataBefore, updataAfter));
    }

    /**
     * 获得客户全部模板的解释合集
     * @param
     * @return
     */
    private HashMap<String, FieldAttrEntity> getCustomerAllTemplate(String corpid, Integer businessType) throws XbbException{
        //全部模版
        Map<String, Object> map = new HashMap<>();
        map.put("corpid", corpid);
        map.put("businessType", businessType);
        map.put("del", DelEnum.NORMAL.getDel());
        map.put("hidden", 0);
        map.put("enable", 1);
        //获得客户的所有处于启用状态的模版
        List<PaasFormEntityExt> paasFormEntityList = paasFormModel.list(map);
        if (CollectionsUtil.isEmpty(paasFormEntityList)){
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_NOT_EXIST);
        }
        //获得所有表单模版的解释，对所有表单模版的系统字段取合集
        Map<String, Object> mapParam = new HashMap<>();
        mapParam.put("corpid", corpid);
        mapParam.put("businessType", businessType);
        List<PaasFormExplainEntity> formExplainList = paasFormExplainModel.list(mapParam);
        if (formExplainList.isEmpty()) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_EXPLAIN_NOT_EXIST);
        }
        //将处于未启用状态的表单解释移除
        List<Long> formIdList = new ArrayList<>();
        paasFormEntityList.forEach(item -> formIdList.add(item.getId()));
        formExplainList.removeIf(item -> !formIdList.contains(item.getFormId()));
        //遍历解释取解释的合集
        List<FieldAttrEntity> allExplainList = new ArrayList<>();
        for (PaasFormExplainEntity paasFormExplainEntity : formExplainList){
            if (Objects.isNull(paasFormExplainEntity)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_EXPLAIN_NOT_EXIST);
            }
            List<FieldAttrEntity> explainList = JSONArray.parseArray(paasFormExplainEntity.getExplains(), FieldAttrEntity.class);

            allExplainList.addAll(explainList);
        }
        HashMap<String, FieldAttrEntity> explainMap = new HashMap<>(allExplainList.size());
        allExplainList.forEach(item -> explainMap.put(item.getAttr(), item));
        return explainMap;
    }

    /**
     * 对attr是子表单还是非子表单的处理
     * @param
     * @param newValues
     * @param attr
     * @param explainMap
     */
    private String attrDeal(List<String> newValues, String attr, Map<String, FieldAttrEntity> explainMap){
        //判断attr和linkAttr是否是子表单
        String[] attrSplit = attr.split("\\.");
        String subAttrName = null;
        if (attrSplit.length > 1){
            //是子表单
            ArrayList<String> subAttrList = new ArrayList<>(Arrays.asList(attrSplit));
            FieldAttrEntity fieldAttrEntity = explainMap.get(subAttrList.get(0));
            if (Objects.nonNull(fieldAttrEntity)){
                SubFormPoJo subForm = fieldAttrEntity.getSubForm();
                if (Objects.nonNull(subForm) && CollectionsUtil.isNotEmpty(subForm.getItems())){
                    List<? extends FieldAttrEntity> items = subForm.getItems();
                    for (FieldAttrEntity fieldAttrEntitySub : items){
                        if (Objects.equals(subAttrList.get(1), fieldAttrEntitySub.getAttr())){
                            subAttrName = fieldAttrEntity.getAttrName() + "." + fieldAttrEntitySub.getAttrName();
                        }
                    }
                }
            }

        }else {
            //非子表单
            FieldAttrEntity fieldAttrEntity = explainMap.get(attr);
            if (Objects.nonNull(fieldAttrEntity)){
                subAttrName = fieldAttrEntity.getAttrName();
            }
        }
        return subAttrName;
    }

    /**
     * 获得表单解释
     * @param formId
     * @param corpid
     * @return
     * @throws XbbException
     */
    private Map<String, FieldAttrEntity> getFormExplain(Long formId, String corpid) throws XbbException{
        PaasFormExplainEntity paasFormExplainEntity = paasFormExplainModel.getByFormId(formId, corpid);
        if(Objects.isNull(paasFormExplainEntity)){
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_EXPLAIN_NOT_EXIST);
        }
        Map<String, FieldAttrEntity> explainMap = ExplainUtil.getExplainMap(paasFormExplainEntity.getExplains(), null);
        return explainMap;
    }


    /**
     * 表单设计，字段解释更改详细日志
     * @param corpid 公司id
     * @param oldObj 旧对象
     * @param newObj 新对象
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogPojo>
     * @throws XbbException
     * @author zcp
     * @date 2019/6/30 22:59
     * @since v1.0
     * @version v1.0
     */
    private List<DetailLogPojo> detailLog4PaasFormExplain(String corpid, JSONObject oldObj, JSONObject newObj) throws XbbException {
        List<DetailLogPojo> detailArr = new ArrayList<>();
        //true 未更改，不需插入详情日志 false 有更改，需插入详情日志
        boolean flag = true;
        for (Map.Entry<String, Object> entry : oldObj.entrySet()) {
            String attr = entry.getKey();
            Object valueOld = entry.getValue();
            Object valueNew = newObj.get(attr);
            if ("explains".equals(attr)) {
                if (chage4Array(valueOld, valueNew)) {
                    List<FieldAttrEntity> oldEexplainList = JsonHelperUtil.parseArray(valueOld.toString(), FieldAttrEntity.class);
                    List<FieldAttrEntity> newEexplainList = valueNew == null ? new ArrayList<>() : JsonHelperUtil.parseArray(valueNew.toString(), FieldAttrEntity.class);
                    //标记已经删除的字段，格式为：字段1(attr_1),字段2(attr_2)
                    detailArr = log4Explain(corpid, detailArr, oldEexplainList, newEexplainList);
                    flag = detailArr.size() <= 0;
                }
            } else if ("updateTime".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
//                    flag = false;
                    detailArr = log4UpdateTime(detailArr, attr, valueOld, valueNew);
                }
            }
        }
        if (flag) {
            //无需插入详情日志
            return null;
        }
        return detailArr;
    }

    /**
     * 应用 详情日志处理
     * @param oldObj 旧对象
     * @param newObj 新对象
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogPojo>
     * @author zcp
     * @date 2019/6/28 17:36
     * @since v1.0
     * @version v1.0
     */
    private List<DetailLogPojo> detailLog4SignInSet(JSONObject oldObj, JSONObject newObj) {
        List<DetailLogPojo> detailArr = new ArrayList<>();
        //true 未更改，不需插入详情日志 false 有更改，需插入详情日志
        boolean flag = true;
        for (Map.Entry<String, Object> entry : oldObj.entrySet()) {
            String attr = entry.getKey();
            Object valueOld = entry.getValue();
            Object valueNew = newObj.get(attr);
            if ("ifAdjust".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Enable(attr, I18nMessageUtil.getMessage(I18nStringConstant.POSITION_FINE_TUNING), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("adjustType".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    Integer before = object2Int(valueOld, SignInSetEnum.AdjustType.ADJUST_TYPE_100.getCode());
                    Integer after = object2Int(valueNew, SignInSetEnum.AdjustType.ADJUST_TYPE_100.getCode());
                    DetailLogPojo detailLogPojo = new DetailLogPojo(attr, I18nMessageUtil.getMessage(I18nStringConstant.ALLOW_EMP_POSITION_FINE_TUNING), SignInSetEnum.AdjustType.getAdjustNameByCode(before), SignInSetEnum.AdjustType.getAdjustNameByCode(after));
                    detailArr.add(detailLogPojo);
                }
            } else if ("customerRequired".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Enable(attr, I18nMessageUtil.getMessage(I18nStringConstant.CUSTOMER_REQUIRED), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("customerLocationUpdate".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Enable(attr, I18nMessageUtil.getMessage(I18nStringConstant.CHANGE_CUSTOMER_LOCATION), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("visibleCustomerDistance".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    Integer before = object2Int(valueOld, SignInSetEnum.VisibleDistance.VISIBLE_DISTANCE_3000.getCode());
                    Integer after = object2Int(valueNew, SignInSetEnum.VisibleDistance.VISIBLE_DISTANCE_3000.getCode());
                    DetailLogPojo detailLogPojo = new DetailLogPojo(attr, I18nMessageUtil.getMessage(I18nStringConstant.VISIBLE_CUSTOMER_RANGE_DISTANCE), SignInSetEnum.VisibleDistance.getVisibleDistanceNameByCode(before), SignInSetEnum.VisibleDistance.getVisibleDistanceNameByCode(after));
                    detailArr.add(detailLogPojo);
                }
            } else if ("updateTime".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    detailArr = log4UpdateTime(detailArr, attr, valueOld, valueNew);
                }
            } else if ("ifFollowRecords".equals(attr)) {
                // todo 陋代码，要重构
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Enable(attr, I18nMessageUtil.getMessage(I18nStringConstant.GENERATE_FOLLOW_RECORDS), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            }
        }
        if (flag) {
            //无需插入详情日志
            return null;
        }
        return detailArr;
    }

    /**
     * 工单模板日志详情
     * @param corpid 公司
     * @param detailLogParamPojo 处理日志详情时，渲染模板数据需要的参数pojo
     * @param oldObj 旧对象
     * @param newObj 新对象
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogPojo>
     * @author zcp
     * @date 2019/7/8 17:37
     * @since v1.0
     * @version v1.0
     */
    private List<DetailLogPojo> detailLog4WorkOrderTemplate(String corpid, DetailLogParamPojo detailLogParamPojo, JSONObject oldObj, JSONObject newObj) {
        //工单模板分类id-分类名称
        Map<String, String> idNameMap = workOrderTemplateCategoryModel.getIdNameMap(corpid);
        List<DetailLogPojo> detailArr = new ArrayList<>();
        //true 未更改，不需插入详情日志 false 有更改，需插入详情日志
        boolean flag = true;
        for (Map.Entry<String, Object> entry : oldObj.entrySet()) {
            String attr = entry.getKey();
            Object valueOld = entry.getValue();
            Object valueNew = newObj.get(attr);
            if ("name".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(attr, I18nMessageUtil.getMessage(I18nStringConstant.TEMPLATE_NAME), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("categoryId".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    String updataBefore = valueOld == null ? "" : idNameMap.getOrDefault(valueOld.toString(), "");
                    String updataAfter = valueNew == null ? "" : idNameMap.getOrDefault(valueNew.toString(), "");
                    DetailLogPojo detailLogPojo = new DetailLogPojo(attr, I18nMessageUtil.getMessage(I18nStringConstant.CATEGORY), updataBefore, updataAfter);
                    detailArr.add(detailLogPojo);
                }
            } else if ("introduction".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(attr, I18nMessageUtil.getMessage(I18nStringConstant.TEMPLATE_INTRODUCTION), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("isFree".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    String threeA = I18nMessageUtil.getMessage(I18nStringConstant.FREE_ASSIGNMENT);
                    String threeB = I18nMessageUtil.getMessage(I18nStringConstant.DESIGNATION_PROCESS);
                    //1为自由指派
                    String updataBefore = formatData4Enable(valueOld, threeA, threeB);
                    String updataAfter = formatData4Enable(valueNew, threeA, threeB);
                    DetailLogPojo detailLogPojo = new DetailLogPojo(attr, I18nMessageUtil.getMessage(I18nStringConstant.TICKET_TYPE), updataBefore, updataAfter);
                    detailArr.add(detailLogPojo);
                }
            } else if ("workflow".equals(attr)) {
                int isFree = object2Int(oldObj.get("isFree"), 0);
                if (isFree == 0) {
                    //只有当isFree=0（指定流程）时，才判断流程是否变更
                    JSONArray oldStageArr = JsonHelperUtil.parseArray(JSON.toJSONString(valueOld));
                    JSONArray newStageArr = JsonHelperUtil.parseArray(JSON.toJSONString(valueNew));
                    if (!CompareUtil.compareArray(oldStageArr, newStageArr)) {
                        flag = false;
                        List<WorkOrderTemplateDetailWorkflowPojo> oldWorkStageList = (List<WorkOrderTemplateDetailWorkflowPojo>) valueOld;
                        List<WorkOrderTemplateDetailWorkflowPojo> newWorkStageList = (List<WorkOrderTemplateDetailWorkflowPojo>) valueNew;
                        String updataBefore = formatWorkOrderTemplateStage(oldWorkStageList);
                        String updataAfter = formatWorkOrderTemplateStage(newWorkStageList);
                        DetailLogPojo detailLogPojo = new DetailLogPojo(attr, I18nMessageUtil.getMessage(I18nStringConstant.TICKET_DETAILS), updataBefore, updataAfter);
                        detailArr.add(detailLogPojo);
                    }
                }
            } else if ("expectedTime".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Second2Hour(attr, I18nMessageUtil.getMessage(I18nStringConstant.EXPECTED_TIME), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("cc".equals(attr)) {
                //非空
                JSONArray ccOld = JsonHelperUtil.parseArray(valueOld.toString());
                JSONArray ccNew = JsonHelperUtil.parseArray(valueNew.toString());
                if (!CompareUtil.compareArray(ccOld, ccNew)) {
                    flag = false;
                    String ccName = "name";
                    String updataBefore = commonHelp.changeArray2String(ccOld, ccName);
                    String updataAfter = commonHelp.changeArray2String(ccNew, ccName);
                    DetailLogPojo detailLogPojo = new DetailLogPojo(attr, I18nMessageUtil.getMessage(I18nStringConstant.CC_PERSON), updataBefore, updataAfter);
                    detailArr.add(detailLogPojo);
                }
            } else if ("scopeRule".equals(attr)) {
                //非空，该字段比较特殊，包含了两部分：评价类型、授权人员（部门,员工）；因此一个字段要切分为两个
                JSONObject scOld = JsonHelperUtil.parseObject(valueOld.toString());
                JSONObject scNew = JsonHelperUtil.parseObject(valueNew.toString());
                //先判断评价类型
                String inside = "inside";
                int insideOld = scOld.getIntValue(inside);
                int insideNew = scNew.getIntValue(inside);
                if (chage4Value(insideOld, insideNew)) {
                    flag = false;
                    String threeA = I18nMessageUtil.getMessage(I18nStringConstant.INTERNAL_EVALUATION_WORK_ORDER);
                    String threeB = I18nMessageUtil.getMessage(I18nStringConstant.EXTERNAL_EVALUATION_WORK_ORDER);
                    String updataBefore = formatData4Enable(insideOld, threeA, threeB);
                    String updataAfter = formatData4Enable(insideNew, threeA, threeB);
                    DetailLogPojo detailLogPojo = new DetailLogPojo(attr, I18nMessageUtil.getMessage(I18nStringConstant.EVALUATION_TYPE), updataBefore, updataAfter);
                    detailArr.add(detailLogPojo);
                }
                //移除评价类型后，再判断授权人员
                scOld.remove(inside);
                scNew.remove(inside);
                if (!CompareUtil.compareJson(scOld, scNew)) {
                    flag = false;
                    detailArr = log4UserAndDep(detailArr, attr, I18nMessageUtil.getMessage(I18nStringConstant.AUTHORIZED_PERSONNEL), scOld, scNew, detailLogParamPojo.getUserMap(), detailLogParamPojo.getDepartmentMap());
                }
            } else if ("updateTime".equals(attr)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    detailArr = log4UpdateTime(detailArr, attr, valueOld, valueNew);
                }
            }
        }
        if (flag) {
            //无需插入详情日志
            return null;
        }
        return detailArr;
    }

    /**
     * 智能助手日志详情
     *
     * @param corpid 公司id
     * @param oldObj 旧对象
     * @param newObj 新对象
     * @throws
     * @return
     * @author hongxiao
     * @date 2019-12-13 16:21
     * @since v1.0
     * @version v1.0
     */
    public List<DetailLogPojo> deatilLog4AiAssistant(String corpid, JSONObject oldObj, JSONObject newObj, List<AiTriggerConditionLogPojo> oldTriggerCondition, List<AiTriggerConditionLogPojo> newTriggerCondition) {
        List<DetailLogPojo> detailArr = new ArrayList<>();
        //true 未更改，不需插入详情日志 false 有更改，需插入详情日志
        boolean flag = true;
        for (Map.Entry<String, Object> entry : oldObj.entrySet()) {
            String key = entry.getKey();
            Object valueOld = entry.getValue();
            Object valueNew = newObj.get(key);
            if ("name".equals(key)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(I18nStringConstant.SMART_ASSISTANT_NAME), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("formId".equals(key)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    PaasFormEntity oldForm = paasFormModel.getByKey(Long.valueOf(valueOld.toString()), corpid);
                    PaasFormEntity newForm = paasFormModel.getByKey(Long.valueOf(valueNew.toString()), corpid);
                    DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(I18nStringConstant.FORM_NAME), oldForm.getName(), newForm.getName());
                    detailArr.add(detailLogPojo);
                }
            } else if ("triggerCondition".equals(key)) {
                if (chage4Array(valueOld, valueNew)) {
                    flag = false;
                    String oldTriggerStr = getAiTriggerCondition(oldTriggerCondition);
                    String newTriggerStr = getAiTriggerCondition(newTriggerCondition);

                    DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(I18nStringConstant.TRIGGERING_CONDITIONS), oldTriggerStr, newTriggerStr);
                    detailArr.add(detailLogPojo);
                }
            } else if ("action".equals(key)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    if (null != valueOld && null != valueNew){
                        SmartActionTypeEnum oldTypeEnum = SmartActionTypeEnum.getByCode(Integer.valueOf(valueOld.toString()));
                        SmartActionTypeEnum newTypeEnum = SmartActionTypeEnum.getByCode(Integer.valueOf(valueNew.toString()));
                        DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(I18nStringConstant.PERFORM_ACTION), oldTypeEnum.getMemo(), newTypeEnum.getMemo());
                        detailArr.add(detailLogPojo);
                    }
                }
            } else if ("strategy".equals(key)) {
                if (chage4Json(valueOld, valueNew)) {
                    flag = false;
                    SmartStrategyPojo oldStrategyPojo = JsonHelperUtil.parseObject(valueOld.toString(), SmartStrategyPojo.class);
                    SmartStrategyPojo newStrategyPojo = JsonHelperUtil.parseObject(valueNew.toString(), SmartStrategyPojo.class);
                    // 执行时间
                    DetailLogPojo timeLogPojo = getAiStrategyTimeInfo(oldStrategyPojo, newStrategyPojo);
                    detailArr.add(timeLogPojo);
                    // 执行次数
                    if (chage4Value(oldStrategyPojo.getStrategyNum(), newStrategyPojo.getStrategyNum())) {
                        DetailLogPojo numLogPojo = log4Normal("strategyNum", I18nMessageUtil.getMessage(I18nStringConstant.NUMBER_OF_EXECUTIONS), oldStrategyPojo.getStrategyNum(), newStrategyPojo.getStrategyNum());
                        detailArr.add(numLogPojo);
                    }
                }
            } else if ("executor".equals(key)) {
                if (chage4Array(valueOld, valueNew)) {
                    flag = false;
                    List<OptionalRangeEntity> oldExecutorList = JsonHelperUtil.parseArray(valueOld.toString(), OptionalRangeEntity.class);
                    List<OptionalRangeEntity> newExecutorList = JsonHelperUtil.parseArray(valueOld.toString(), OptionalRangeEntity.class);
                    StringBuffer oldNameBuffer = new StringBuffer();
                    StringBuffer newNameBuffer = new StringBuffer();
                    for (OptionalRangeEntity entity : oldExecutorList) {
                        if (oldNameBuffer.length() == BasicConstant.ZERO) {
                            oldNameBuffer.append(entity.getName());
                        } else {
                            oldNameBuffer.append("、" + entity.getName());
                        }
                    }
                    for (OptionalRangeEntity entity : newExecutorList) {
                        if (newNameBuffer.length() == BasicConstant.ZERO) {
                            newNameBuffer.append(entity.getName());
                        } else {
                            newNameBuffer.append("、" + entity.getName());
                        }
                    }
                    DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(I18nStringConstant.EXECUTIVE), oldNameBuffer.toString(), newNameBuffer.toString());
                    detailArr.add(detailLogPojo);
                }
            } else if ("smsExecutor".equals(key)) {
                if (chage4Array(valueOld, valueNew)) {
                    flag = false;
                    List<SmartFieldAttrPojo> oldSmsExecutorList = JsonHelperUtil.parseArray(valueOld.toString(), SmartFieldAttrPojo.class);
                    List<SmartFieldAttrPojo> newSmsExecutorList = JsonHelperUtil.parseArray(valueNew.toString(), SmartFieldAttrPojo.class);
                    StringBuffer oldNameBuffer = new StringBuffer();
                    StringBuffer newNameBuffer = new StringBuffer();
                    for (SmartFieldAttrPojo pojo : oldSmsExecutorList) {
                        if (oldNameBuffer.length() == BasicConstant.ZERO) {
                            oldNameBuffer.append(pojo.getAttrName());
                        } else {
                            oldNameBuffer.append("、" + pojo.getAttrName());
                        }
                    }
                    for (SmartFieldAttrPojo pojo : newSmsExecutorList) {
                        if (newNameBuffer.length() == BasicConstant.ZERO) {
                            newNameBuffer.append(pojo.getAttrName());
                        } else {
                            newNameBuffer.append("、" + pojo.getAttrName());
                        }
                    }
                    DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(I18nStringConstant.SMS_EXECUTIVE), oldNameBuffer.toString(), newNameBuffer.toString());
                    detailArr.add(detailLogPojo);
                }
            } else if ("memo".equals(key)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(I18nStringConstant.MEMO), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            }
        }
        if (flag) {
            return null;
        }
        return detailArr;
    }

    /**
     * 消息推送设置日志详情页
     *
     * @param oldObj 旧字段对应的值对象
     * @param newObj 新字段对应的值对象
     * @return
     * @throws
     * @author hongxiao
     * @date 2020-01-02 13:43
     * @version v4.10.0
     * @since v4.10.0
     */
    private List<DetailLogPojo> detailLog4MessagePushSet(JSONObject oldObj, JSONObject newObj) {
        List<DetailLogPojo> detailArr = new ArrayList<>();

        //true 未更改，不需插入详情日志 false 有更改，需插入详情日志
        boolean flag = true;
        for (Map.Entry<String, Object> entry : oldObj.entrySet()) {
            String key = entry.getKey();
            Object valueOld = entry.getValue();
            Object valueNew = newObj.get(key);

            if ("pushTime".equals(key)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    JSONObject oldTimeObj = JSONObject.parseObject(valueOld.toString());
                    JSONObject newTimeObj = JSONObject.parseObject(valueNew.toString());
                    // 时间值
                    Integer oldTimeDay = oldTimeObj.getInteger("period");
                    Integer newTimeDay = newTimeObj.getInteger("period");
                    Integer oldTime = oldTimeObj.getInteger("time");
                    Integer newTime = newTimeObj.getInteger("time");
                    // 时间类型
                    Integer oldTimeType = oldTimeObj.getInteger("timeType");
                    Integer newTimeType = newTimeObj.getInteger("timeType");
                    // 类型（需要小时或天）
                    Integer oldType = oldTimeObj.getInteger("type");
                    Integer newType = newTimeObj.getInteger("type");
                    JSONArray timeArray = oldTimeObj.getJSONArray("timeArray");
                    String oldMemo = "";
                    String newMemo = "";
                    if (Objects.equals(oldTimeType, BasicConstant.THREE) && Objects.equals(oldType, BasicConstant.TWO)) {
                        oldMemo = oldTime / TimeConstant.SECONDS_PER_HOUR + I18nMessageUtil.getMessage(CommonConstant.POINT);
                    } else if (Objects.equals(oldType, BasicConstant.ONE)) {
                        String oldDayStr = oldTimeDay / TimeConstant.SECONDS_PER_DAY + I18nMessageUtil.getMessage(CommonConstant.DAY);
                        oldMemo = getPushTimeTypeName(timeArray, oldTimeType) + oldDayStr;
                    }
                    if (Objects.equals(newTimeType, BasicConstant.THREE) && Objects.equals(newType, BasicConstant.TWO)) {
                        newMemo = newTime / TimeConstant.SECONDS_PER_HOUR + I18nMessageUtil.getMessage(CommonConstant.POINT);

                    } else if (Objects.equals(newType, BasicConstant.ONE)) {
                        String newDayStr = newTimeDay / TimeConstant.SECONDS_PER_DAY + I18nMessageUtil.getMessage(CommonConstant.DAY);
                        newMemo = getPushTimeTypeName(timeArray, newTimeType) + newDayStr;
                    }

                    DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(I18nStringConstant.PUSH_TIME), oldMemo, newMemo);
                    detailArr.add(detailLogPojo);
                }
            } else if ("pushObject".equals(key)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;

                    JSONObject oldObject = JSONObject.parseObject(valueOld.toString());
                    JSONObject newObject = JSONObject.parseObject(valueNew.toString());

                    // type (推送/不推送给哪些人)
                    JSONArray oldTypeArray = oldObject.getJSONArray("type");
                    JSONArray newTypeArray = newObject.getJSONArray("type");
                    if (Objects.nonNull(oldTypeArray) && Objects.nonNull(newTypeArray)) {
                        getPushTypeDetail(oldTypeArray, newTypeArray, detailArr);
                    }

                    // 上级主管
                    JSONObject oldManager = oldObject.getJSONObject("manager");
                    JSONObject newManager = newObject.getJSONObject("manager");
                    if (Objects.nonNull(oldManager) && Objects.nonNull(newManager)) {
                        getPushOptionalDetail("manager", oldManager, newManager, detailArr);
                    }

                    // 自定义推送人员
                    JSONObject oldUser = oldObject.getJSONObject("user");
                    JSONObject newUser = newObject.getJSONObject("user");
                    if (Objects.nonNull(oldUser) && Objects.nonNull(newUser)) {
                        getPushOptionalDetail("user", oldUser, newUser, detailArr);
                    }
                    // 推送角色
                    JSONObject oldRole = oldObject.getJSONObject("role");
                    JSONObject newRole = newObject.getJSONObject("role");
                    if (Objects.nonNull(oldRole) && Objects.nonNull(newRole)) {
                        getPushOptionalDetail("role", oldRole, newRole, detailArr);
                    }
                    // 推送部门
                    JSONObject oldDep = oldObject.getJSONObject("dep");
                    JSONObject newDep = newObject.getJSONObject("dep");
                    if (Objects.nonNull(oldDep) && Objects.nonNull(newDep)) {
                        getPushOptionalDetail("dep", oldDep, newDep, detailArr);
                    }
                    //多选组件
                    JSONObject oldMulti = oldObject.getJSONObject("multi");
                    JSONObject newMulti = newObject.getJSONObject("multi");
                    if (Objects.nonNull(oldMulti) && Objects.nonNull(newMulti)) {
                        getPushOptionalDetail("multi", oldMulti, newMulti, detailArr);
                    }
                }
            }
        }
        if (flag) {
            return null;
        }
        return detailArr;
    }

    /**
     * 获取消息推送设置：时间名称
     *
     * @param timeArray 推送时间可选项
     * @param timeType  推送时间类型
     * @return
     * @throws
     * @author hongxiao
     * @date 2020-01-02 17:10
     * @version v4.10.0
     * @since v4.10.0
     */
    private String getPushTimeTypeName(JSONArray timeArray, Integer timeType) {
        if (Objects.isNull(timeArray) || timeArray.isEmpty()) {
            return "";
        }
        for (Object obj : timeArray) {
            JSONObject timeObj = (JSONObject) obj;
            Integer id = timeObj.getInteger("id");
            if (Objects.equals(id, timeType)) {
                return timeObj.getString("name");
            }
        }
        return "";
    }

    /**
     * 添加消息推送设置：推送人类型（负责人、协同人）
     *
     * @param oldTypeArray 旧字段对应的值对象
     * @param newTypeArray 新字段对应的值对象
     * @param detailArr    返回值
     * @return
     * @throws
     * @author hongxiao
     * @date 2020-01-02 17:10
     * @version v4.10.0
     * @since v4.10.0
     */
    private void getPushTypeDetail(JSONArray oldTypeArray, JSONArray newTypeArray, List<DetailLogPojo> detailArr) {
        Map<String, JSONObject> oldMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<String, JSONObject> newMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if (Objects.nonNull(oldTypeArray) && !oldTypeArray.isEmpty()) {
            for (Object obj : oldTypeArray) {
                JSONObject typeObj = (JSONObject) obj;
                oldMap.put(typeObj.getString("alias"), typeObj);
            }
        }
        if (Objects.nonNull(newTypeArray) && !newTypeArray.isEmpty()) {
            for (Object obj : newTypeArray) {
                JSONObject typeObj = (JSONObject) obj;
                newMap.put(typeObj.getString("alias"), typeObj);
            }
        }


        for (Map.Entry<String, JSONObject> entry : oldMap.entrySet()) {
            String alias = entry.getKey();
            JSONObject oldValue = entry.getValue();
            JSONObject newValue = newMap.get(alias);
            if (chage4Value(oldValue, newValue)) {
                String oldEnableStr = Objects.equals(oldValue.getInteger("enable"), BasicConstant.NO_USE) ? I18nMessageUtil.getMessage(I18nStringConstant.NOT_PUSH) : I18nMessageUtil.getMessage(I18nStringConstant.PUSH);
                String newEnableStr = Objects.equals(newValue.getInteger("enable"), BasicConstant.NO_USE) ? I18nMessageUtil.getMessage(I18nStringConstant.NOT_PUSH) : I18nMessageUtil.getMessage(I18nStringConstant.PUSH);

                DetailLogPojo detailLogPojo = log4Normal(alias, oldValue.getString("name"), oldEnableStr, newEnableStr);
                detailArr.add(detailLogPojo);
            }
        }

    }

    /**
     * 消息推送设置：主管、人员、角色、部门
     *
     * @param alias     别名
     * @param oldObj    旧字段对应的值对象
     * @param newObj    新字段对应的值对象
     * @param detailArr 返回值
     * @return
     * @throws
     * @author hongxiao
     * @date 2020-01-02 18:40
     * @version
     * @since
     */
    private void getPushOptionalDetail(String alias, JSONObject oldObj, JSONObject newObj, List<DetailLogPojo> detailArr) {
        if (!chage4Value(oldObj, newObj)) {
            return;
        }
        StringBuffer oldNameBuffer = new StringBuffer();
        StringBuffer newNameBuffer = new StringBuffer();


        Map<String, String> keyMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        keyMap.put("manager", I18nMessageUtil.getMessage(I18nStringConstant.SUPERVISOR));
        keyMap.put("user", I18nMessageUtil.getMessage(I18nStringConstant.CUSTOM_PUSH_PERSON));
        keyMap.put("role", I18nMessageUtil.getMessage(I18nStringConstant.PUSH_ROLE));
        keyMap.put("dep", I18nMessageUtil.getMessage(I18nStringConstant.PUSH_DEPARTMENT));
        keyMap.put("multi", I18nMessageUtil.getMessage(I18nStringConstant.CUSTOM_PUSH_PERSON));


        JSONArray oldValueList = oldObj.getJSONArray("value");
        JSONArray newValueList = newObj.getJSONArray("value");

        if (Objects.nonNull(oldValueList) && !oldValueList.isEmpty()) {
            for (Object obj : oldValueList) {
                JSONObject userObj = (JSONObject) obj;
                if (oldNameBuffer.length() == BasicConstant.ZERO) {
                    oldNameBuffer.append(userObj.getString("name"));
                } else {
                    oldNameBuffer.append("、" + userObj.getString("name"));
                }
            }
        }

        if (Objects.nonNull(newValueList) && !newValueList.isEmpty()) {
            for (Object obj : newValueList) {
                JSONObject userObj = (JSONObject) obj;
                if (newNameBuffer.length() == BasicConstant.ZERO) {
                    newNameBuffer.append(userObj.getString("name"));
                } else {
                    newNameBuffer.append("、" + userObj.getString("name"));
                }
            }
        }

        DetailLogPojo detailLogPojo = log4Normal(alias, keyMap.get(alias), oldNameBuffer.toString(), newNameBuffer.toString());
        detailArr.add(detailLogPojo);

    }

    /**
     * 处理名称、图标等直接比较值是否一致的字段更改日志
     * @param key 字段英文名，即数据库内字段名
     * @param name 字段名称
     * @param valueOld 旧字段对应的值对象
     * @param valueNew 新字段对应的值对象
     * @return com.xbongbong.pro.log.pojo.DetailLogPojo
     * @author zcp
     * @date 2019/6/28 16:25
     * @since v1.0
     * @version v1.0
     */
    private DetailLogPojo log4Normal(String key, String name, Object valueOld, Object valueNew) {
        String updataBefore = object2String(valueOld);
        String updataAfter = object2String(valueNew);
        return new DetailLogPojo(key, name, updataBefore, updataAfter);
    }

    /**
     * 处理类似预计耗时（库内存的是秒数，显示时要转为小时）的字段变更日志
     * @param key 字段英文名，即数据库内字段名
     * @param name 字段名称
     * @param valueOld 旧字段对应的值对象
     * @param valueNew 新字段对应的值对象
     * @return com.xbongbong.pro.log.pojo.DetailLogPojo
     * @author zcp
     * @date 2019/7/10 14:55
     * @since v1.0
     * @version v1.0
     */
    private DetailLogPojo log4Second2Hour(String key, String name, Object valueOld, Object valueNew) {
        //秒数转为小时
        Integer valueOldInt = object2Int(valueOld, 0);
        Integer valueNewInt = object2Int(valueNew, 0);
        String updataBefore = Arith.div(valueOldInt, TimeConstant.SECONDS_PER_HOUR, 2) + I18nMessageUtil.getMessage(CommonConstant.HOUR);
        String updataAfter = Arith.div(valueNewInt, TimeConstant.SECONDS_PER_HOUR, 2) + I18nMessageUtil.getMessage(CommonConstant.HOUR);
        return new DetailLogPojo(key, name, updataBefore, updataAfter);
    }

    /**
     * 成员单选，转为员工名称
     * @param detailArr 日志信息
     * @param key 字段英文名，即数据库内字段名
     * @param name 字段名称
     * @param valueOld 旧字段对应的值对象
     * @param valueNew 新字段对应的值对象
     * @param userMap 员工id-员工实体
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogPojo>
     * @author zcp
     * @date 2019/7/2 23:01
     * @since v1.0
     * @version v1.0
     */
    private List<DetailLogPojo> log4User(List<DetailLogPojo> detailArr, String key, String name, Object valueOld, Object valueNew, Map<String, UserEntity> userMap) {
        String userIdBefore = object2String(valueOld);
        String userIdAfter = object2String(valueNew);
        if (Objects.equals(userIdBefore, userIdAfter)) {
            //无更改
            return detailArr;
        }
        UserEntity userBefore = userMap.get(userIdBefore);
        UserEntity userAfter = userMap.get(userIdAfter);
        if (userBefore == userAfter) {
            return detailArr;
        }
        String updataBefore = userBefore == null ? "" : userBefore.getName();
        String updataAfter = userAfter == null ? "" : userAfter.getName();
        detailArr.add(new DetailLogPojo(key, name, updataBefore, updataAfter));
        return detailArr;
    }

    /**
     * 成员多选，转为员工名称
     * @param key 字段英文名，即数据库内字段名
     * @param name 字段名称
     * @param valueOld 旧字段对应的值对象
     * @param valueNew 新字段对应的值对象
     * @param userMap 员工id-员工实体
     * @return com.xbongbong.pro.log.pojo.DetailLogPojo
     * @author zcp
     * @date 2019/7/2 23:14
     * @since v1.0
     * @version v1.0
     */
    private DetailLogPojo log4UserGroup(String key, String name, Object valueOld, Object valueNew, Map<String, UserEntity> userMap) {
        Set userIdBefore = object2Set(valueOld);
        Set userIdAfter = object2Set(valueNew);

        String updataBefore = formatUserNameInfo(userMap, userIdBefore);
        String updataAfter = formatUserNameInfo(userMap, userIdAfter);
        return new DetailLogPojo(key, name, updataBefore, updataAfter);
    }

    /**
     * 部门多选，转为部门名称
     * @param detailArr 日志信息
     * @param key 字段英文名，即数据库内字段名
     * @param name 字段名称
     * @param valueOld 旧字段对应的值对象，格式为：{"dept":["3","5"],"inside":1,"user":["1"]}
     * @param valueNew 新字段对应的值对象，格式为：{"dept":["3","5"],"inside":1,"user":["1"]}
     * @param userMap 员工id-员工实体
     * @param departmentMap 部门id-部门实体
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogPojo>
     * @author zcp
     * @date 2019/7/9 23:29
     * @since v1.0
     * @version v1.0
     */
    private List<DetailLogPojo> log4UserAndDep(List<DetailLogPojo> detailArr, String key, String name, JSONObject valueOld, JSONObject valueNew, Map<String, UserEntity> userMap, Map<String, DepartmentEntity> departmentMap) {
        String keyDep = OptionalRangeEnum.DEPT.getValue();
        String keyUser = OptionalRangeEnum.USER.getValue();
        List<Long> depIdsOld = JsonHelperUtil.parseArray(valueOld.getString(keyDep), Long.class);
        List<String> userIdsOld = JsonHelperUtil.parseArray(valueOld.getString(keyUser), String.class);
        List<Long> depIdsNew = JsonHelperUtil.parseArray(valueNew.getString(keyDep), Long.class);
        List<String> userIdsNew = JsonHelperUtil.parseArray(valueNew.getString(keyUser), String.class);

        //更改前的部门名、员工名
        String userBefore = formatUserNameInfo(userMap, userIdsOld);
        String depBefore = formatDepNameInfo(departmentMap, depIdsOld);
        //更改后的部门名、员工名
        String userAfter = formatUserNameInfo(userMap, userIdsNew);
        String depAfter = formatDepNameInfo(departmentMap, depIdsNew);

        //把更改前的部门、员工名分别拼起来
        String updataBefore = format4UserAndDep(userBefore, depBefore);
        String updataAfter = format4UserAndDep(userAfter, depAfter);
        detailArr.add(new DetailLogPojo(key, name, updataBefore, updataAfter));
        return detailArr;
    }

    /**
     * 部门单选，转为部门名称
     * @param detailArr 日志信息
     * @param key 字段英文名，即数据库内字段名
     * @param name 字段名称
     * @param valueOld 旧字段对应的值对象
     * @param valueNew 新字段对应的值对象
     * @param departmentMap 部门id-部门实体
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogPojo>
     * @author zcp
     * @date 2019/7/2 23:29
     * @since v1.0
     * @version v1.0
     */
    private List<DetailLogPojo> log4Dept(List<DetailLogPojo> detailArr, String key, String name, Object valueOld, Object valueNew, Map<String, DepartmentEntity> departmentMap) {
        String userIdBefore = object2String(valueOld);
        String userIdAfter = object2String(valueNew);
        if (Objects.equals(userIdBefore, userIdAfter)) {
            //无更改
            return detailArr;
        }
        DepartmentEntity before = departmentMap.get(userIdBefore);
        DepartmentEntity after = departmentMap.get(userIdAfter);
        if (before == after) {
            return detailArr;
        }
        String updataBefore = before == null ? "" : before.getName();
        String updataAfter = after == null ? "" : after.getName();
        detailArr.add(new DetailLogPojo(key, name, updataBefore, updataAfter));
        return detailArr;
    }

    /**
     * 部门多选，转为部门名称
     * @param key 字段英文名，即数据库内字段名
     * @param name 字段名称
     * @param valueOld 旧字段对应的值对象
     * @param valueNew 新字段对应的值对象
     * @param departmentMap 部门id-部门实体
     * @return com.xbongbong.pro.log.pojo.DetailLogPojo
     * @author zcp
     * @date 2019/7/2 23:39
     * @since v1.0
     * @version v1.0
     */
    private DetailLogPojo log4DeptGroup(String key, String name, Object valueOld, Object valueNew, Map<String, DepartmentEntity> departmentMap) {
        Set depIdBefore = object2Set(valueOld);
        Set depIdAfter = object2Set(valueNew);

        String updataBefore = formatDepNameInfo(departmentMap, depIdBefore);
        String updataAfter = formatDepNameInfo(departmentMap, depIdAfter);
        return new DetailLogPojo(key, name, updataBefore, updataAfter);
    }

    /**
     * 处理开启/关闭的字段（0关闭、1开启）更改日志
     * @param key 字段英文名，即数据库内字段名
     * @param name 字段名称
     * @param valueOld 旧字段对应的值对象
     * @param valueNew 新字段对应的值对象
     * @return com.xbongbong.pro.log.pojo.DetailLogPojo
     * @author zcp
     * @date 2019/6/28 17:26
     * @since v1.0
     * @version v1.0
     */
    private DetailLogPojo log4Enable(String key, String name, Object valueOld, Object valueNew) {
        String updataBefore = formatData4Enable(valueOld);
        String updataAfter = formatData4Enable(valueNew);
        return new DetailLogPojo(key, name, updataBefore, updataAfter);
    }

    /**
     * 处理更新时间的变更
     * @param detailArr 日志信息
     * @param key 字段名
     * @param valueOld 旧值
     * @param valueNew 新值
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogPojo>
     * @author zcp
     * @date 2019/6/28 14:49
     * @since v1.0
     * @version v1.0
     */
    private List<DetailLogPojo> log4UpdateTime(List<DetailLogPojo> detailArr, String key, Object valueOld, Object valueNew){
        return log4Time(detailArr, key, I18nMessageUtil.getMessage(CommonConstant.UPDATE_TIME), valueOld, valueNew, DateTimeEnum.DATETIME.getType());
    }


    public List<DetailLogPojo> log4UpdateTimeDuplicate(List<DetailLogPojo> detailArr, String key, Object valueOld, Object valueNew){
        return log4Time(detailArr, key, I18nMessageUtil.getMessage(CommonConstant.UPDATE_TIME), valueOld, valueNew, DateTimeEnum.DATETIME.getType());
    }
    /**
     *
     * 处理时间的变更
     * @param detailArr 日志信息
     * @param key 字段名
     * @param attrName 字段名
     * @param valueOld 旧值
     * @param valueNew 新值
     * @param dateType 时间格式
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogPojo>
     * @author zcp
     * @date 2019/7/3 16:35
     * @version v1.0
     * @since v1.0
     */
    private List<DetailLogPojo> log4Time(List<DetailLogPojo> detailArr, String key, String attrName, Object valueOld, Object valueNew, String dateType) {
        String updataBefore = "";
        String updataAfter = "";
        Long timeOld = object2Long(valueOld);
        Long timeNew = object2Long(valueNew);
        dateType = StringUtil.isEmpty(dateType) ? DateTimeEnum.DATETIME.getType() : dateType;
        SimpleDateFormat SDF = new SimpleDateFormat(dateType);
        if (timeOld > 0) {
            updataBefore = DateTimeUtil.getStringEpochSecond(timeOld, SDF);
        }
        if (timeNew > 0) {
            updataAfter = DateTimeUtil.getStringEpochSecond(timeNew, SDF);
        }
        if (Objects.equals(updataBefore, updataAfter)) {
            return detailArr;
        }
        detailArr.add(new DetailLogPojo(key, attrName, updataBefore, updataAfter));
        return detailArr;
    }

    /**
     * 表单设计--字段修改日志
     * @param corpid 公司id
     * @param detailArr 日志对象
     * @param oldEexplainList 旧字段解释list
     * @param newEexplainList 新字段解释list
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogPojo>
     * @throws XbbException
     * @author zcp
     * @date 2019/6/30 22:57
     * @since v1.0
     * @version v1.0
     */
    private List<DetailLogPojo> log4Explain(String corpid, List<DetailLogPojo> detailArr, List<FieldAttrEntity> oldEexplainList, List<FieldAttrEntity> newEexplainList) throws XbbException {
        String removeAttr = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, I18nMessageUtil.getMessage(I18nStringConstant.CLEAR_FIELD));
        if (newEexplainList == null || newEexplainList.size() == 0) {
            DetailLogPojo delDetailLogPojo = delAttr(oldEexplainList, removeAttr);
            if (delDetailLogPojo != null) {
                detailArr.add(delDetailLogPojo);
            }
            return detailArr;
        }
        //------封装容器detailLogDataPojo：员工、部门、角色容器
        DetailLogParamPojo detailLogParamPojo = commonHelp.getDetailLogParamPojo4UserDepRole(corpid, DetailLogParamEnum.getEnumList4All());
        //字段编辑修改以新的为准
        newEexplainList.forEach(after -> {
            String attrNew = after.getAttr();
            Integer fieldType = after.getFieldType();
            String onlyOne = I18nMessageUtil.getMessage(I18nStringConstant.ONLY_ONE_UPLOAD_ALLOWED);
            if (Objects.equals(FieldTypeEnum.IMAGE.getType(), fieldType)) {
                onlyOne += I18nMessageUtil.getMessage(I18nStringConstant.PICTURES) + ":";
            } else if (Objects.equals(FieldTypeEnum.UPLOAD.getType(), fieldType)) {
                onlyOne += I18nMessageUtil.getMessage(I18nStringConstant.ATTACHMENTS) + ":";
            } else {
                onlyOne += I18nMessageUtil.getMessage(I18nStringConstant.FILE) + ":";
            }
            boolean addFlag = true;
            StringBuilder updataBefore = new StringBuilder();
            StringBuilder updataAfter = new StringBuilder();
            //与旧字段比较
            Iterator<FieldAttrEntity> iterator = oldEexplainList.iterator();
            while (iterator.hasNext()) {
                FieldAttrEntity before = iterator.next();
                String attrOld = before.getAttr();
                //只比较该字段
                if (Objects.equals(attrNew, attrOld)) {
                    //存在该字段，非新增
                    addFlag = false;
                    //字段判断过则从old内移除（剩余的都是禁用/删除的字段）
                    iterator.remove();
                    //有更新，则塞入具体更新信息
                    formatExplainInfo(corpid,detailLogParamPojo, before, after, updataBefore, updataAfter, onlyOne);
                    break;
                }
            }
            if (addFlag) {
                //拼上方头括号
                String add = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, I18nMessageUtil.getMessage(I18nStringConstant.NEW_FIELDS));
                detailArr.add(new DetailLogPojo(attrNew, after.getAttrName(), "", add));
            } else {
                if (updataBefore.length() > 0 || updataAfter.length() > 0) {
                    //用方头括号包住
                    String uBefore = updataBefore.length() > 0 ? String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, updataBefore.toString()) : "";
                    String uAfter = updataAfter.length() > 0 ? String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, updataAfter.toString()) : "";
                    detailArr.add(new DetailLogPojo(attrNew, after.getAttrName(), uBefore, uAfter));
                }
            }
        });
        //如果oldEexplainList还有，而新的里面没有，说明这些字段都删除了
        if (oldEexplainList.size() > 0) {
            DetailLogPojo delDetailLogPojo = delAttr(oldEexplainList, removeAttr);
            if (delDetailLogPojo != null) {
                detailArr.add(delDetailLogPojo);
            }
        }
        return detailArr;
    }

    /**
     * 表单数据-修改日志
     * @param parentAttrName 父字段名
     * @param detailArr 日志对象
     * @param redundantTemplateTypeEnum 表单枚举
     * @param detailLogParamPojo 处理日志详情时，渲染模板数据需要的参数pojo
     * @param dataOld 实体内的data-旧值
     * @param dataNew 实体内的data-新值
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogPojo>
     * @throws XbbException
     * @author zcp
     * @date 2019/7/2 23:59
     * @since v1.0
     * @version v1.0
     */
    private List<DetailLogPojo> log4Data(String corpid, String parentAttrName, List<DetailLogPojo> detailArr, RedundantTemplateTypeEnum redundantTemplateTypeEnum, DetailLogParamPojo detailLogParamPojo, JSONObject dataOld, JSONObject dataNew) throws XbbException {
        if (CompareUtil.compareJson(dataOld, dataNew)) {
            //该字段对应的值相同，无更改
            return detailArr;
        }
        //客户、合同有特殊数据字典
        boolean customerFlag = Objects.equals(RedundantTemplateTypeEnum.CUSTOMER, redundantTemplateTypeEnum);
        boolean contractFlag = Objects.equals(RedundantTemplateTypeEnum.CONTRACT, redundantTemplateTypeEnum);
        boolean bomFlag = Objects.equals(RedundantTemplateTypeEnum.BOMBILL, redundantTemplateTypeEnum);
        boolean communicatePlanFlag = Objects.equals(RedundantTemplateTypeEnum.COMMUNICATE_PLAN, redundantTemplateTypeEnum);

        Map<String, FieldAttrEntity> explainMap = detailLogParamPojo.getExplainMap();
        Map<String, UserEntity> userMap = detailLogParamPojo.getUserMap();
        Map<String, DepartmentEntity> departmentMap = detailLogParamPojo.getDepartmentMap();
        Map<String, Map<String, String>> specialDictionaryMap = detailLogParamPojo.getSpecialDictionaryMap();
        Map<String, String> dictionaryMap;
        //不为空，则拼上“-”号
        parentAttrName = StringUtil.isEmpty(parentAttrName) ? "" : parentAttrName + SymbolConstant.COLON_CHINESE;
        for (Map.Entry<String, FieldAttrEntity> entry : explainMap.entrySet()) {
            String attr = entry.getKey();
            FieldAttrEntity fieldAttrEntity = entry.getValue();
            Integer fieldType = fieldAttrEntity.getFieldType();
            //bom清单的版本隐藏字段不跳过：bom的版本是一个文本加勾选的组件，而勾选是一个隐藏字段控制的
            boolean bomVersion = bomFlag && Objects.equals(attr, BomBillEnum.VERSION_LINK_TEXT.getAttr());
            boolean hideJump = ShowTypeEnum.hideJump(fieldAttrEntity.getShowType());
            if (hideJump && !bomVersion && !communicatePlanFlag) {
                continue;
            }
            if (FieldTypeEnum.filterType(fieldType)) {
                continue;
            }
            Object valueOld = dataOld == null ? null : dataOld.get(attr);
            Object valueNew = dataNew == null ? null : dataNew.get(attr);
            //判断当前字段的值是否更改
            if (CompareUtil.compareObject(valueOld, valueNew)) {
                //该字段对应的值相同，无更改
                continue;
            }
            //拼上父字段名称：“父字段名-子字段名”
            String attrName = parentAttrName + fieldAttrEntity.getAttrName();
            List<ItemPoJo> itemPojoList = fieldAttrEntity.getItems();
            FieldTypeEnum fieldTypeEnum = FieldTypeEnum.getFieldTypeEnum(fieldType);
            String updataBefore;
            String updataAfter;
            switch (fieldTypeEnum) {
                case TEXT:
                case NUM:
                case TEXTAREA:
                case BIRTHDAY:
                case LINK_DATA_MULTI:
                case CATEGORY_ID:
                    //文本加勾选（bom表单内的字段，勾选是用另外一个隐藏字段控制，需特殊处理）
                case TEXT_CHECK:
                case RICH_TEXT_EDITOR:
                    if (bomVersion) {
                        //bom的版本是一个文本加勾选的组件，而勾选是一个隐藏字段（类型为NUM）控制的
                        attrName = getAttrName(explainMap, BomBillEnum.VERSION.getAttr(), BomBillEnum.VERSION.getAttrName());
                        attrName = attrName + "-" + I18nMessageUtil.getMessage(I18nStringConstant.SET_DEFAULT_VERSION);
                        updataBefore = formatData4Enable(valueOld);
                        updataAfter = formatData4Enable(valueNew);
                        detailArr.add(new DetailLogPojo(attr, attrName, updataBefore, updataAfter));
                    } else {
                        //单行文本、数字、多行文本、生日、产品分类，直接展示（关联数据LINK_DATA为自定义的关联，也直接展示id）
                        detailArr.add(new DetailLogPojo(attr, attrName, object2String(valueOld), object2String(valueNew)));
                    }
                    break;
                case DATETIME:
                    log4Time(detailArr, attr, attrName, valueOld, valueNew, fieldAttrEntity.getDateType());
                    break;
                case COMBO:
                case RADIO_GROUP:
                    //客户状态、合同状态、物流公司需要读取tb_data_dictionary
                    boolean comboFlag = (customerFlag && Objects.equals(CustomerManagementEnum.TYPE.getAttr(), attr)) || (contractFlag && Objects.equals(ContractEnum.STATUS.getAttr(), attr)) || (contractFlag && Objects.equals(ContractEnum.EXPRESS_NAME.getAttr(), attr));
                    if (comboFlag) {
                        dictionaryMap = specialDictionaryMap.get(attr);
                    } else {
                        dictionaryMap = commonHelp.listToMap(itemPojoList);
                    }
                    if (Objects.nonNull(dictionaryMap)) {
                        updataBefore = "";
                        updataAfter = "";
                        if(valueOld!=null){
                            updataBefore = valueOld.toString();
                            String updataBeforeStr = dictionaryMap.getOrDefault(valueOld.toString(), "");
                            if(StringUtil.isNotEmpty(updataBeforeStr)){
                                updataBefore = updataBeforeStr;
                            }
                        }
                        if(valueNew!=null){
                            updataAfter = valueNew.toString();
                            String updataAfterStr = dictionaryMap.getOrDefault(valueNew.toString(), "");
                            if(StringUtil.isNotEmpty(updataAfterStr)){
                                updataAfter = updataAfterStr;
                            }
                        }
                        if(!CompareUtil.compareObject(updataBefore, updataAfter)){
                            detailArr.add(new DetailLogPojo(attr, attrName, updataBefore, updataAfter));
                        }
                    }
                    break;
                case COMBO_CHECK:
                case CHECKBOX_GROUP:
                    //下拉复选框、复选框，直接拼接展示所选值
                    Integer comboType = fieldAttrEntity.getComboType();
                    if (Objects.equals(ComboTypeEnum.LINKFORM.getType(), comboType)) {
                        //数据联动的，直接拼接更改的值即可
                        String value = "value";
                        updataBefore = commonHelp.changeArray2String(valueOld);
                        updataAfter = commonHelp.changeArray2String(valueNew);
                    } else {
                        dictionaryMap = commonHelp.listToMap(itemPojoList);
                        updataBefore = info4GroupCheck(dictionaryMap, valueOld);
                        updataAfter = info4GroupCheck(dictionaryMap, valueNew);
                    }
                    detailArr.add(new DetailLogPojo(attr, attrName, updataBefore, updataAfter));
                    break;
                case STAR:
                    Integer starOld = object2Int(valueOld, 0);
                    Integer starNew = object2Int(valueNew, 0);
                    updataBefore = StarEnum.getTextByValue(starOld, "");
                    updataAfter = StarEnum.getTextByValue(starNew, "");
                    detailArr.add(new DetailLogPojo(attr, attrName, updataBefore, updataAfter));
                    break;
                case ADDRESS:
                case LOCATION:
                    JSONObject addressBefore = valueOld == null ? null : JsonHelperUtil.parseObject(valueOld.toString());
                    JSONObject addressAfter = valueNew == null ? null : JsonHelperUtil.parseObject(JSONObject.toJSONString(valueNew));
                    String addressOld = commonHelp.formatAddress(addressBefore);
                    String addressNew = commonHelp.formatAddress(addressAfter);
                    if (!Objects.equals(addressOld, addressNew)) {
                        //定位，经纬度不同，名称可能相同
                        detailArr.add(new DetailLogPojo(attr, attrName, addressOld, addressNew));
                    }
                    break;
                case SWITCH:
                    //开关
                    detailArr.add(log4Enable(attr, attrName, valueOld, valueNew));
                    break;
                case SELECT_DEPARTMENT:
                case USER:
                    detailArr = log4User(detailArr, attr, attrName, valueOld, valueNew, userMap);
                    break;
                case USER_GROUP:
                    detailArr.add(log4UserGroup(attr, attrName, valueOld, valueNew, userMap));
                    break;
                case DEPT:
                    log4Dept(detailArr, attr, attrName, valueOld, valueNew, departmentMap);
                    break;
                case DEPT_GROUP:
                    detailArr.add(log4DeptGroup(attr, attrName, valueOld, valueNew, departmentMap));
                    break;
                case SUB_FORM:
                    Map<String, FieldAttrEntity> subExplainMap = ExplainUtil.getExplainMap(JSON.toJSONString(fieldAttrEntity.getSubForm().getItems()), null);
                    //子表单的解释，需要置入一个新的pojo内，防止影响父表单的解释
                    DetailLogParamPojo subDetailLogParamPojo = BeanUtil.copyProperties(detailLogParamPojo, DetailLogParamPojo.class, false);
                    subDetailLogParamPojo.setExplainMap(subExplainMap);
                    //子表单的数据为数组（子表单可以有多行数据）
                    JSONArray arrayOld = valueOld == null ? new JSONArray() : JsonHelperUtil.parseArray(valueOld.toString());
                    JSONArray arrayNew = valueNew == null ? new JSONArray() : JsonHelperUtil.parseArray(valueNew.toString());
                    int sizeOld = arrayOld.size();
                    int sizeNew = arrayNew.size();
                    int size = sizeOld >= sizeNew ? sizeOld : sizeNew;
                    StringBuilder sb = new StringBuilder();
                    sb.append(attrName).append(SymbolConstant.MINUS);
                    for (int i = 0; i < size; i++) {
                        String index = I18nMessageUtil.getMessage(CommonConstant.DI) + (i + 1) + I18nMessageUtil.getMessage(CommonConstant.ROW);
                        JSONObject subOld = sizeOld <= i ? null: arrayOld.getJSONObject(i);
                        JSONObject subNew = sizeNew <= i ? null: arrayNew.getJSONObject(i);
                        //拼上序号：“父字段名-子字段名-1”
                        String parentName = sb.toString() + index;
                        if (subNew == null || subNew.size() == 0) {
                            updataAfter = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, I18nMessageUtil.getMessage(I18nStringConstant.DATA_ALL_EMPTY));
                            detailArr.add(new DetailLogPojo(BusinessConstant.PASS_FORM_EXPLAIN_ATTR_DEL, parentName, "", updataAfter));
                            continue;
                        }
                        log4Data(corpid, parentName, detailArr, redundantTemplateTypeEnum, subDetailLogParamPojo, subOld, subNew);
                    }
                    break;
                case CONTACT_NUMBER:
                    updataBefore = commonHelp.formatPhone(valueOld);
                    updataAfter = commonHelp.formatPhone(valueNew);
                    detailArr.add(new DetailLogPojo(attr, attrName, updataBefore, updataAfter));
                    break;
                case SWITCH_TEXT:
                    //开关+文本
                    updataBefore = commonHelp.formatSwitchText(valueOld);
                    updataAfter = commonHelp.formatSwitchText(valueNew);
                    detailArr.add(new DetailLogPojo(attr, attrName, updataBefore, updataAfter));
                    break;
                case SINGLE_SUB_FORM:
                    if (Objects.equals(attr, InvoiceEnum.CUSTOMER_INVOICE.getAttr())) {
                        updataBefore = commonHelp.formatInvoice(valueOld);
                        updataAfter = commonHelp.formatInvoice(valueNew);
                    } else {
                        updataBefore = commonHelp.formatInvoiceExpress(valueOld);
                        updataAfter = commonHelp.formatInvoiceExpress(valueNew);
                    }
                    detailArr.add(new DetailLogPojo(attr, attrName, updataBefore, updataAfter));
                    break;
                case LINK_BUSINESS_SINGLE:
                case COMBO_LINK_BUSINESS:
                case LINK_BUSINESS_MULTI:
                    detailArr = formatData4LinkBusinessSingle(detailArr, redundantTemplateTypeEnum, fieldTypeEnum, attr, attrName, dataOld, dataNew);
                    break;
                case LABEL:
                    List<Long> labelIdListOld = JSON.parseArray(JSON.toJSONString(valueOld), Long.class);
                    List<Long> labelIdListNew = JSON.parseArray(JSON.toJSONString(valueNew), Long.class);
                    List<String> nameListOld = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    if(CollectionsUtil.isNotEmpty(labelIdListOld)){
                        List<LabelEntity> labelListOld = labelModel.getByLabelIds(labelIdListOld,corpid);
                        labelListOld.forEach(labelEntity -> nameListOld.add(labelEntity.getName()));
                    }
                    updataBefore = StringUtils.join(nameListOld, "，");

                    List<String> nameListNew = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    if(CollectionsUtil.isNotEmpty(labelIdListNew)){
                        List<LabelEntity> labelListNew = labelModel.getByLabelIds(labelIdListNew,corpid);
                        labelListNew.forEach(labelEntity -> nameListNew.add(labelEntity.getName()));
                    }
                    updataAfter = StringUtils.join(nameListNew, "，");

                    detailArr.add(new DetailLogPojo(attr, attrName, updataBefore, updataAfter));
                    break;
                case IMAGE:
                    log4Image(detailArr,valueOld, valueNew, attr, attrName);
                    break;
                case UPLOAD:
                    log4Upload(detailArr,valueOld, valueNew, attr, attrName);
                    break;
                case LINK_DATA:
                    log4LinkData(detailArr, valueOld, valueNew, attr, attrName);
                    break;
                default:
                    break;
            }
        }
        return detailArr;
    }
    private void log4LinkData(List<DetailLogPojo> detailArr,Object valueOld,Object valueNew,String attr,String attrName){
        //  关联数据，直接展示（关联数据LINK_DATA为自定义的关联，也直接展示id）
        valueOld = Objects.equals(0, valueOld) ? null : valueOld;
        valueNew = Objects.equals(0, valueNew) ? null : valueNew;
        if (!Objects.equals(valueNew, valueOld)) {
            detailArr.add(new DetailLogPojo(attr, attrName, object2String(valueOld), object2String(valueNew)));
        }
    }

    /**
     * 处理表单数据标题（attrName或者content）
     * @param value 标题对象
     * @return java.lang.String
     * @author zcp
     * @date 2019/6/29 18:59
     * @since v1.0
     * @version v1.0
     */
    private String info4FormTitlePojo(Object value) {
        if (value == null) {
            return "";
        }
        List<FormTitlePoJo> formTitlePojoList = JsonHelperUtil.parseArray(value.toString(), FormTitlePoJo.class);
        StringBuilder updata = new StringBuilder();
        formTitlePojoList.forEach(item -> {
            if (updata.length() != 0) {
                updata.append(SymbolConstant.COMMA_CHINESE);
            }
            if (StringUtil.isNotEmpty(item.getAttr())) {
                updata.append(item.getAttrName());
                //拼接上字段名
                appendAttr(updata, item.getAttr());
            } else if (StringUtil.isNotEmpty(item.getContent())) {
                updata.append(item.getContent());
            }
        });
        return updata.toString();
    }

    /**
     * 处理表单数据摘要、数据标签（只需要attrName）
     * @param value 摘要/标签
     * @return java.lang.String
     * @author zcp
     * @date 2019/6/29 19:16
     * @since v1.0
     * @version v1.0
     */
    private String info4SummaryDataPojo(Object value) {
        if (value == null) {
            return "";
        }
        List<SummaryDataPoJo> summaryDataPojoList = JsonHelperUtil.parseArray(value.toString(), SummaryDataPoJo.class);
        StringBuilder updata = new StringBuilder();
        summaryDataPojoList.forEach(item -> {
            if (updata.length() != 0) {
                updata.append(SymbolConstant.COMMA_CHINESE);
            }
            updata.append(item.getAttrName());
            //拼接上字段名
            appendAttr(updata, item.getAttr());
        });
        return updata.toString();
    }

    /**
     * 下拉复选框、复选框组
     * @param dictionaryMap 数据字典map  value-text
     * @param valueArr 数据字典的value集合-数组型
     * @return java.lang.String
     * @author zcp
     * @date 2019/7/3 16:20
     * @since v1.0
     * @version v1.0
     */
    private String info4GroupCheck(Map<String, String> dictionaryMap, Object valueArr) {
        if (valueArr == null) {
            return "";
        }
        List<String> valueList = JsonHelperUtil.parseArray(valueArr.toString(), String.class);
        StringBuilder updataInfo = new StringBuilder();
        for (String value : valueList) {
            if (updataInfo.length() > 0) {
                updataInfo.append(SymbolConstant.COMMA_ENGLISH);
            }
            String text = dictionaryMap.get(value);
            if (StringUtil.isNotEmpty(text)) {
                updataInfo.append(text);
            } else {
                updataInfo.append(value);
            }
        }
        return updataInfo.toString();
    }

    /**
     * 塞入字段解释的日志变化信息
     * @param detailLogParamPojo 参数容器：员工、部门、角色
     * @param before FieldAttrEntity，旧字段解释对象
     * @param after FieldAttrEntity，新字段解释对象
     * @param updataBefore 更改前信息
     * @param updataAfter 更改后信息
     * @param onlyOne 仅允许上传一个提示语（文件和图片提示不一样，在while循环外部提前拼好该值）
     * @author zcp
     * @date 2019/6/30 22:34
     * @since v1.0
     * @version v1.0
     */
    private void formatExplainInfo(String corpid, DetailLogParamPojo detailLogParamPojo, FieldAttrEntity before, FieldAttrEntity after, StringBuilder updataBefore, StringBuilder updataAfter, String onlyOne) {
        formatAttrInfo(updataBefore, updataAfter,I18nMessageUtil.getMessage(I18nStringConstant.TITLE) + ":", before.getAttrName(), after.getAttrName());
        formatAttrInfo(updataBefore, updataAfter,I18nMessageUtil.getMessage(I18nStringConstant.DESCRIPTION) + ":", before.getMemo(), after.getMemo());
        //【字段权限-可见】相关日志处理
        formatAttrInfo4AttrRule(true, detailLogParamPojo, updataBefore, updataAfter, before, after);
        //【字段权限-可编辑】相关日志处理
        formatAttrInfo4AttrRule(false, detailLogParamPojo, updataBefore, updataAfter, before, after);
        // 字段查重相关日志处理
        formatAttrDuplicateEnable(corpid,updataBefore, updataAfter,I18nMessageUtil.getMessage(I18nStringConstant.CHECK_DUPLICATE) + ":", before, after);
        //formatAttrInfo4Enable(updataBefore, updataAfter,I18nMessageUtil.getMessage(I18nStringConstant.CHECK_DUPLICATE) + ":", before.getNoRepeat(), after.getNoRepeat());
        formatAttrInfo4Enable(updataBefore, updataAfter,I18nMessageUtil.getMessage(I18nStringConstant.CHECK_REQUIRED) + ":", before.getRequired(), after.getRequired());
        // patternType 字段类型
        String patternTypeOld = PatternEnum.getPatternDesc(before.getPatternType());
        String patternTypeNew = PatternEnum.getPatternDesc(after.getPatternType());
        formatAttrInfo4int(updataBefore, updataAfter,I18nMessageUtil.getMessage(I18nStringConstant.FIELD_TYPE) + ":", before.getPatternType(), after.getPatternType(), patternTypeOld, patternTypeNew);
        formatAttrInfo4Enable(updataBefore, updataAfter,I18nMessageUtil.getMessage(I18nStringConstant.CHECK_INTEGERS) + ":", before.getIntegerOnly(), after.getIntegerOnly());
        formatAttrInfo(updataBefore, updataAfter,I18nMessageUtil.getMessage(I18nStringConstant.CHECK_NUMERICAL_PRECISION) + ":", before.getAccuracy(), after.getAccuracy());
        //限制数值范围处理，当numericalLimitsFlag不相等时，展示numericalLimits
        String numericalLimitsOld = formatNumericalLimits(before.getNumericalLimits());
        String numericalLimitsNew = formatNumericalLimits(after.getNumericalLimits());
        formatAttrInfo4Connect(updataBefore, updataAfter,I18nMessageUtil.getMessage(I18nStringConstant.LIMITED_VALUE_RANGE) + ":", before.getNumericalLimitsFlag(), after.getNumericalLimitsFlag(), numericalLimitsOld, numericalLimitsNew);
        formatAttrInfo(updataBefore, updataAfter,I18nMessageUtil.getMessage(I18nStringConstant.TIME_TYPE) + ":", before.getDateType(), after.getDateType());
        //comboType，下拉框
        String comboTypeOld = ComboTypeEnum.getComboTypeMemo(before.getComboType());
        String comboTypeNew = ComboTypeEnum.getComboTypeMemo(after.getComboType());
        formatAttrInfo4int(updataBefore, updataAfter,I18nMessageUtil.getMessage(I18nStringConstant.OPTION) + ":", before.getComboType(), after.getComboType(), comboTypeOld, comboTypeNew);

        formatAttrInfo(updataBefore, updataAfter,I18nMessageUtil.getMessage(I18nStringConstant.LINEAR) + ":", before.getLineType(), after.getLineType());
        formatAttrInfo4Enable(updataBefore, updataAfter,I18nMessageUtil.getMessage(I18nStringConstant.SHOW_DETAILED_ADDRESS) + ":", before.getShowDetailAddress(), after.getShowDetailAddress());
        //定位中心处理，当locationCenterFlag不相等时，展示locationCenter
        String locationCenterOld = formatLocationCenter(before.getLocationCenter());
        String locationCenterNew = formatLocationCenter(after.getLocationCenter());
        formatAttrInfo4Connect(updataBefore, updataAfter, I18nMessageUtil.getMessage(I18nStringConstant.SET_POSITIONING_RANGE), before.getLocationCenterFlag(), after.getLocationCenterFlag(), locationCenterOld, locationCenterNew);
        //微调
        TuningPoJo tuningOld = before.getTuning() == null? new TuningPoJo(): before.getTuning();
        TuningPoJo tuningNew = after.getTuning() == null? new TuningPoJo(): after.getTuning();
        String radiusOld = tuningOld.getRadius() == null? "500": tuningOld.getRadius().toString();
        String radiusNew = tuningNew.getRadius() == null? "500": tuningNew.getRadius().toString();
        formatAttrInfo4Enable(updataBefore, updataAfter,I18nMessageUtil.getMessage(I18nStringConstant.POSITION_ALLOW_FINE_TUNING) + ":", tuningOld.getEnable(), tuningNew.getEnable());
        formatAttrInfo(updataBefore, updataAfter,I18nMessageUtil.getMessage(I18nStringConstant.POSITION_ALLOW_FINE_TUNING_DISTANCE) + ":", radiusOld, radiusNew);

        formatAttrInfo(updataBefore, updataAfter, I18nMessageUtil.getMessage(I18nStringConstant.MAXIMUM_NUMBER_FILES), before.getMaxFileCount(), after.getMaxFileCount());
        formatAttrInfo4Enable(updataBefore, updataAfter, onlyOne, before.getOnlyOneFile(), after.getOnlyOneFile());
        formatAttrInfo4Enable(updataBefore, updataAfter, I18nMessageUtil.getMessage(I18nStringConstant.PHOTO_UPLOAD) + ":", before.getOnlyCamera(), after.getOnlyCamera());
        formatAttrInfo4Enable(updataBefore, updataAfter, I18nMessageUtil.getMessage(I18nStringConstant.AUTO_COMPRESS_PICTURES) + ":", before.getAutoCompress(), after.getAutoCompress());
        //summaryFlag，汇总类型
        formatAttrInfo4Enable(updataBefore, updataAfter, I18nMessageUtil.getMessage(I18nStringConstant.DATA_SUMMARY_VALUE) + ":", before.getSummaryFlag(), after.getSummaryFlag());
        formatAttrInfo4Enable(updataBefore, updataAfter, I18nMessageUtil.getMessage(I18nStringConstant.ENABLE_AS_AMOUNT) + ":", before.getAmountFlag(), after.getAmountFlag());
        // setType 权限
        String setTypeOld = SetTypeEnum.getSetTypeDesc(before.getSetType());
        String setTypeNew = SetTypeEnum.getSetTypeDesc(after.getSetType());
        formatAttrInfo4int(updataBefore, updataAfter,I18nMessageUtil.getMessage(I18nStringConstant.AUTHORITY) + ":", before.getSetType(), after.getSetType(), setTypeOld, setTypeNew);
        //负责人唯一权限
        formatAttrInfo4Enable(updataBefore, updataAfter,I18nMessageUtil.getMessage(I18nStringConstant.OWNER_UNIQUE) + ":", before.getMultiple(), after.getMultiple());
        //协同人表单权限
        formatAttrInfo4CoPer(updataBefore, updataAfter,I18nMessageUtil.getMessage(I18nStringConstant.COUSER_PERMISSION) + ":", before.getFormPermission(), after.getFormPermission());

    }

    /**
     * 处理判重日志
     * @author lijiangren
     * @date 2022-01-25 19 23
     */
    private void formatAttrDuplicateEnable(String corpid, StringBuilder updataBefore, StringBuilder updataAfter, String title, FieldAttrEntity before, FieldAttrEntity after) {
        try {
            Integer valueBefore = before.getNoRepeat();
            Integer valueAfter = after.getNoRepeat();

            Integer beforeRepeatType = Objects.isNull(before.getCustomRepeatType()) ? 0 : before.getCustomRepeatType();
            Integer afterRepeatType = Objects.isNull(after.getCustomRepeatType()) ? 0 : after.getCustomRepeatType();
            String infoOld = null;
            String infoNew = null;
            if (!Objects.equals(valueBefore, valueAfter)) {
                infoOld = formatCustomRepeat(corpid,before);
                infoNew = formatCustomRepeat(corpid,after);

                updateInfo4String(updataBefore, "", infoOld);
                updateInfo4String(updataAfter, "", infoNew);
            } else {
                if (Objects.equals(valueBefore,1)) {
                    if (Objects.equals(beforeRepeatType,0) && Objects.equals(beforeRepeatType, afterRepeatType)) {
                        return;
                    }

                    if (!(Objects.equals(beforeRepeatType,1) && Objects.equals(beforeRepeatType, afterRepeatType)) ) {
                        if (Objects.equals(beforeRepeatType,2) && Objects.equals(beforeRepeatType, afterRepeatType)) {
                            List<CustomRecheckRulePojo> beforeRecheckRules = CollectionsUtil.isNotEmpty(before.getCustomRecheckRules()) ? before.getCustomRecheckRules() : new ArrayList<>();
                            List<CustomRecheckRulePojo> afterRecheckRules = CollectionsUtil.isNotEmpty(after.getCustomRecheckRules()) ? after.getCustomRecheckRules() : new ArrayList<>();
                            Map beforeMap = new HashMap();
                            beforeMap.put("rules",beforeRecheckRules);
                            Map afterMap = new HashMap();
                            afterMap.put("rules",afterRecheckRules);
                            JSONObject oldJson = JSONObject.parseObject(JSONObject.toJSONString(beforeMap));
                            JSONObject newJson = JSONObject.parseObject(JSONObject.toJSONString(afterMap));
                            if (CompareUtil.compareJson(oldJson, newJson)) {
                                //两个相同，则无更新，无需处理
                                return;
                            } else {
                                infoOld = formatCustomRepeat(corpid,before);
                                infoNew = formatCustomRepeat(corpid,after);
                            }
                        } else {
                            infoOld = formatCustomRepeat(corpid,before);
                            infoNew = formatCustomRepeat(corpid,after);
                        }
                    }
                }

                updateInfo4String(updataBefore, "", infoOld);
                updateInfo4String(updataAfter, "", infoNew);
            }
        } catch (Exception e) {

        }
    }

    /**
     * 处理判重日志
     * @author lijiangren
     * @date 2022-01-25 19 23
     */
    private String formatCustomRepeat(String corpid, FieldAttrEntity entity) throws Exception {
        String descvalue = null;
        Integer valueNoRepeat = entity.getNoRepeat();
        Integer repeatType = Objects.isNull(entity.getCustomRepeatType()) ? 0 : entity.getCustomRepeatType();
        if (Objects.equals(valueNoRepeat, 0)) {
            descvalue = I18nMessageUtil.getMessage(EnableEnum.CLOSE.getMemo());
        } else {
            //开启，获取开启的配置
            if (Objects.equals(repeatType,0)) {
                //模板内判重
                descvalue = I18nMessageUtil.getMessage(EnableEnum.OPEN.getMemo());
            } else if (Objects.equals(repeatType,1)) {
                //模板内判重
                descvalue = I18nMessageUtil.getMessage(RepeatTypeEnum.REPEAT_SINGLE.getMemo());
            } else {
                List<CustomRecheckRulePojo> customRecheckRules = entity.getCustomRecheckRules();
                if (CollectionsUtil.isNotEmpty(customRecheckRules)) {
                    Set<Long> formIds = customRecheckRules.stream().map(CustomRecheckRulePojo::getFormId).collect(Collectors.toSet());
                    List<PaasFormEntityExt> paasFormEntityExtList = paasFormModel.getByIdIn(formIds, corpid);
                    List<PaasFormExplainEntity> paasFormExplainEntityList = paasFormExplainModel.getByFormIdIn(formIds, corpid);
                    descvalue = I18nMessageUtil.getMessage(RepeatTypeEnum.REPEAT_ALL.getMemo()) + ":";
                    for (CustomRecheckRulePojo customRecheckRule : customRecheckRules) {
                        PaasFormEntityExt paasFormEntityExt = paasFormEntityExtList.stream().filter(item-> Objects.equals(item.getId(),customRecheckRule.getFormId())).findAny().orElse(null);
                        PaasFormExplainEntity paasFormExplainEntity = paasFormExplainEntityList.stream().filter(item-> Objects.equals(item.getFormId(),customRecheckRule.getFormId())).findAny().orElse(null);
                        if (Objects.nonNull(paasFormEntityExt) && Objects.nonNull(paasFormExplainEntity)) {
                            Map<String, FieldAttrEntity> explainMap = ExplainUtil.getAllSubFormExplainMap(paasFormExplainEntity.getExplains());
                            FieldAttrEntity fieldAttrEntity = explainMap.get(customRecheckRule.getAttr());
                            if (Objects.nonNull(fieldAttrEntity)) {
                                descvalue = descvalue + String.format(I18nMessageUtil.getMessage(MessageConstant.CUSTOM_CHECK_REPEAT), paasFormEntityExt.getName(), fieldAttrEntity.getAttrName()) + "、";
                            } else {
                                descvalue = descvalue + String.format(I18nMessageUtil.getMessage(MessageConstant.CUSTOM_CHECK_REPEAT), customRecheckRule.getFormId(), customRecheckRule.getAttr()) + "、";
                            }
                        }
                    }
                }
            }
            if (Objects.nonNull(descvalue) && descvalue.contains("、")) {
                descvalue = descvalue.substring(0, descvalue.lastIndexOf("、"));
            }
        }
        return descvalue;
    }

    /**
     * 判断字段属性是否相等，不相等则塞入更改前、更改后
     * @param updataBefore 更改前
     * @param updataAfter 更改后
     * @param title 字段属性标题描述
     * @param valueBefore 旧值
     * @param valueAfter 新值
     * @author zcp
     * @date 2019/6/30 20:22
     * @since v1.0
     * @version v1.0
     */
    private void formatAttrInfo(StringBuilder updataBefore, StringBuilder updataAfter, String title, Object valueBefore, Object valueAfter) {
        if (!Objects.equals(valueBefore, valueAfter)) {
            //判空，非空时才拼接信息
            updateInfo(updataBefore, title, valueBefore);
            updateInfo(updataAfter, title, valueAfter);
        }
    }

    /**
     * 判断字段（0、1类型字段）属性是否相等，不相等则塞入更改前、更改后
     * @param updataBefore 更改前
     * @param updataAfter 更改后
     * @param title 字段属性标题描述
     * @param valueBefore 旧值
     * @param valueAfter 新值
     * @author zcp
     * @date 2019/6/30 20:30
     * @since v1.0
     * @version v1.0
     */
    private void formatAttrInfo4Enable(StringBuilder updataBefore, StringBuilder updataAfter, String title, Integer valueBefore, Integer valueAfter) {
        if (!Objects.equals(valueBefore, valueAfter)) {
            String infoOld = Objects.equals(valueBefore, 1) ? I18nMessageUtil.getMessage(EnableEnum.CLOSE.getMemo()) : I18nMessageUtil.getMessage(EnableEnum.OPEN.getMemo());
            String infoNew = Objects.equals(valueAfter, 1) ? I18nMessageUtil.getMessage(EnableEnum.CLOSE.getMemo()) : I18nMessageUtil.getMessage(EnableEnum.OPEN.getMemo());
            updateInfo4String(updataBefore, title, infoOld);
            updateInfo4String(updataAfter, title, infoNew);
        }
    }

    /**
     * 判断字段（0、1类型字段）属性是否相等，不相等则塞入更改前、更改后
     * @param updataBefore 更改前
     * @param updataAfter 更改后
     * @param title 字段属性标题描述
     * @param valueBefore 旧值
     * @param valueAfter 新值
     * @author zcp
     * @date 2019/6/30 20:30
     * @since v1.0
     * @version v1.0
     */
    private void formatAttrInfo4CoPer(StringBuilder updataBefore, StringBuilder updataAfter, String title, Integer valueBefore, Integer valueAfter) {
        if (!Objects.equals(valueBefore, valueAfter)) {
            String infoOld = Objects.equals(valueBefore, 0) ? CoUserPermissionEnum.READ.getValue() : CoUserPermissionEnum.EDIT.getValue();
            String infoNew = Objects.equals(valueAfter, 0) ? CoUserPermissionEnum.READ.getValue() : CoUserPermissionEnum.EDIT.getValue();
            updateInfo4String(updataBefore, title, infoOld);
            updateInfo4String(updataAfter, title, infoNew);
        }
    }

    /**
     * 判断字段（int类型字段）属性是否相等，不相等则把更改的值（int对应的中文描述）塞入更改前、更改后
     * @param updataBefore 更改前
     * @param updataAfter 更改后
     * @param title 字段属性标题描述
     * @param valueBefore 旧值（int，不能直接展示，需用其它字段的值展示）
     * @param valueAfter 新值（int，不能直接展示，需用其它字段的值展示）
     * @param infoBefore 要显示的旧值
     * @param infoAfter 要显示的新值
     * @author zcp
     * @date 2019/6/30 20:53
     * @since v1.0
     * @version v1.0
     */
    private void formatAttrInfo4int(StringBuilder updataBefore, StringBuilder updataAfter, String title, Integer valueBefore, Integer valueAfter, String infoBefore, String infoAfter) {
        //开启才有值，关闭直接显示空
        String tempInfoOld = Objects.equals(valueBefore, 1) ? infoBefore : "";
        String tempInfoNew = Objects.equals(valueAfter, 1) ? infoAfter : "";
        formatAttrInfo4Connect(updataBefore, updataAfter, title, valueBefore, valueAfter, tempInfoOld, tempInfoNew);
    }

    /**
     * 不止要判断开关，还需要判断开关关联的输入框（或其它关联属性设置）是否变化，只要其中一个不同，则都为有更新
     * @param updataBefore 更改前
     * @param updataAfter 更改后
     * @param title 字段属性标题描述
     * @param valueBefore 旧值（int，不能直接展示，需用其它字段的值展示）
     * @param valueAfter 新值（int，不能直接展示，需用其它字段的值展示）
     * @param infoBefore 要显示的旧值
     * @param infoAfter 要显示的新值
     * @author zcp
     * @date 2019/7/1 21:00
     * @since v1.0
     * @version v1.0
     */
    private void formatAttrInfo4Connect(StringBuilder updataBefore, StringBuilder updataAfter, String title, Integer valueBefore, Integer valueAfter, String infoBefore, String infoAfter) {
        if (!Objects.equals(valueBefore, valueAfter) || !Objects.equals(infoBefore, infoAfter)) {
            updateInfo4String(updataBefore, title, infoBefore);
            updateInfo4String(updataAfter, title, infoAfter);
        }
    }

    /**
     * 处理“字段设置-【可见/编辑】”的设置内容变更情况
     * @param flag true可见 false可编辑
     * @param detailLogParamPojo 参数容器：员工、部门、角色
     * @param updataBefore 更改前
     * @param updataAfter 更改后
     * @param before FieldAttrEntity，旧字段解释对象
     * @param after FieldAttrEntity，新字段解释对象
     * @author zcp
     * @date 2019/7/11 22:11
     * @since v1.0
     * @version v1.0
     */
    private void formatAttrInfo4AttrRule(boolean flag, DetailLogParamPojo detailLogParamPojo, StringBuilder updataBefore, StringBuilder updataAfter, FieldAttrEntity before, FieldAttrEntity after) {
        Integer enableBefore;
        Integer enableAfter;
        Integer scopeEnableBefore;
        Integer scopeEnableAfter;
        String memo;
        VisibleRulePoJo ruleOld;
        VisibleRulePoJo ruleNew;
        if (flag) {
            memo = I18nMessageUtil.getMessage(I18nStringConstant.FIELD_PERMISSIONS_VISIBLE);
            enableBefore = before.getVisible();
            enableAfter = after.getVisible();
            scopeEnableBefore = before.getVisibleScopeEnable();
            scopeEnableAfter = after.getVisibleScopeEnable();
            ruleOld = before.getVisibleScopeRule();
            ruleNew = after.getVisibleScopeRule();
        } else {
            memo = I18nMessageUtil.getMessage(I18nStringConstant.FIELD_PERMISSIONS_EDIT);
            enableBefore = before.getEditable();
            enableAfter = after.getEditable();
            scopeEnableBefore = before.getEditableAdvanceEnable();
            scopeEnableAfter = after.getEditableAdvanceEnable();
            ruleOld = before.getEditableRule();
            ruleNew = after.getEditableRule();
        }
        //【可见/编辑】
        String title = memo + ":";
        formatAttrInfo4Enable(updataBefore, updataAfter, title, enableBefore, enableAfter);
        if (Objects.equals(enableAfter, BasicConstant.ZERO)) {
            //字段权限的【可见/编辑】改为了不可见，其它的高级设置等变化就不再标识
            return;
        }
        //【可见/编辑-高级设置】
        title = memo + I18nMessageUtil.getMessage(I18nStringConstant.ADVANCED_SETTINGS);
        formatAttrInfo4Enable(updataBefore, updataAfter, title, scopeEnableBefore, scopeEnableAfter);
        if (Objects.equals(scopeEnableAfter, BasicConstant.ZERO)) {
            //字段权限的【可见/编辑-高级设置】改为了不可见，其它的高级设置内容变化就不再标识
            return;
        }
        //【可见/编辑-高级设置】设置的具体规则
        JSONObject oldJson = getJson4VisibleRule(ruleOld);
        JSONObject newJson = getJson4VisibleRule(ruleNew);
        if (CompareUtil.compareJson(oldJson, newJson)) {
            //两个相同，则无更新，无需处理
            return;
        }
        title = memo + I18nMessageUtil.getMessage(I18nStringConstant.ADVANCED_SETTINGS);
        //分别封装设置的更改前后内容
        String infoBefore = formatAttrRule(flag, detailLogParamPojo, ruleOld);
        String infoAfter = formatAttrRule(flag, detailLogParamPojo, ruleNew);
        updateInfo4String(updataBefore, title, infoBefore);
        updateInfo4String(updataAfter, title, infoAfter);
    }

    /**
     * 处理saas的特殊字段，主要有：LINK_BUSINESS_SINGLE、COMBO_LINK_BUSINESS
     * @param detailArr 日志信息
     * @param redundantTemplateTypeEnum 表单枚举
     * @param fieldTypeEnum 字段类型
     * @param attr 字段名
     * @param attrName 字段名
     * @param dataOld 旧值
     * @param dataNew 新值
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogPojo>
     * @author zcp
     * @date 2019/7/4 16:51
     * @since v1.0
     * @version v1.0
     */
    private List<DetailLogPojo> formatData4LinkBusinessSingle(List<DetailLogPojo> detailArr, RedundantTemplateTypeEnum redundantTemplateTypeEnum, FieldTypeEnum fieldTypeEnum, String attr, String attrName, JSONObject dataOld, JSONObject dataNew) {
        dataOld = dataOld == null ? new JSONObject() : dataOld;
        dataNew = dataNew == null ? new JSONObject() : dataNew;
        String linkAttr;
        switch (redundantTemplateTypeEnum) {
            case ASSEMBLE:
                linkAttr = AssembleEnum.changeToHide4Link(attr);
                break;
            case BOMBILL:
                linkAttr = BomBillEnum.changeToHide4Link(attr);
                break;
            case COMMUNICATE_PLAN:
                linkAttr = CommunicatePlanEnum.changeToHide4Link(attr);
                break;
            case CONTACT:
                linkAttr = ContactEnum.changeToHide4Link(attr);
                break;
            case CONTRACT:
                linkAttr = ContractEnum.changeToHide4Link(attr);
                break;
            case QUOTATION:
                linkAttr = QuotationEnum.changeToHide4Link(attr);
                break;
            case COMMUNICATE:
                linkAttr = CustomerCommunicateEnum.changeToHide4Link(attr);
                break;
            case CUSTOMER:
                linkAttr = CustomerManagementEnum.changeToHide4Link(attr);
                break;
            case INSTOCK:
                linkAttr = InstockEnum.changeToHide4Link(attr);
                break;
            case INVENTORY:
                linkAttr = InventoryEnum.changeToHide4Link(attr);
                break;
            case INVOICE:
                linkAttr = InvoiceEnum.changeToHide4Link(attr);
                break;
            case PURCHASE_INVOICE:
                linkAttr = PurchaseInvoiceEnum.changeToHide4Link(attr);
                break;
            case OUTSTOCK:
                linkAttr = OutstockEnum.changeToHide4Link(attr);
                break;
            case PAYMENT:
                linkAttr = PaymentEnum.changeToHide4Link(attr);
                break;
            case PAYMENT_SHEET:
                linkAttr = PaymentSheetBaseEnum.changeToHide4Link(attr);
                break;
            case PAY_PLAN:
                linkAttr = PayPlanEnum.changeToHide4Link(attr);
                break;
            case PAY_SHEET:
                linkAttr = PaySheetEnum.changeToHide4Link(attr);
                break;
            case PRODUCT:
                linkAttr = ProductEnum.changeToHide4Link(attr);
                break;
            case PRODUCTIONORDER:
                linkAttr = ProductionOrderEnum.changeToHide4Link(attr);
                break;
            case PURCHASE:
                linkAttr = PurchaseEnum.changeToHide4Link(attr);
                break;
            case REFUND:
                linkAttr = RefundEnum.changeToHide4Link(attr);
                break;
            case RETURNED_PURCHASE:
                linkAttr = ReturnedPurchaseEnum.changeToHide4Link(attr);
                break;
            case SALES_OPPORTUNITY:
                linkAttr = SalesOpportunityEnum.changeToHide4Link(attr);
                break;
            case JXC_COMMUNICATE:
                linkAttr = SupplierCommunicateEnum.changeToHide4Link(attr);
                break;
            case SUPPLIER_CONTACT:
                linkAttr = SupplierContactEnum.changeToHide4Link(attr);
                break;
            case TRANSFER:
                linkAttr = TransferEnum.changeToHide4Link(attr);
                break;
            case WORKORDER:
                linkAttr = WorkOrderEnum.changeToHide4Link(attr);
                break;
            default:
                return detailArr;
        }
        String updataBefore;
        String updataAfter;
        if (Objects.equals(fieldTypeEnum, FieldTypeEnum.LINK_BUSINESS_MULTI)) {
            updataBefore = commonHelp.changeArray2String(dataOld.get(linkAttr));
            updataAfter = commonHelp.changeArray2String(dataNew.get(linkAttr));
        } else {
            updataBefore = dataOld.getString(linkAttr);
            updataAfter = dataNew.getString(linkAttr);
        }
        DetailLogPojo detailLogPojo = new DetailLogPojo(attr, attrName, updataBefore, updataAfter);
        detailArr.add(detailLogPojo);
        return detailArr;
    }

    /**
     * 拼接更新信息（Object对象）
     * @param updateInfo 更新信息
     * @param title 字段属性标题描述
     * @param value 更新前/后的信息
     * @author zcp
     * @date 2019/7/1 22:32
     * @since v1.0
     * @version v1.0
     */
    private void updateInfo(StringBuilder updateInfo, String title, Object value) {
        //判空，非空时才拼接信息
        if (value != null && value.toString().length() > 0) {
            //非空时拼上分号
            if (updateInfo.length() > 0) {
                updateInfo.append(SymbolConstant.SEMICOLON_CHINESE).append(SymbolConstant.BR);
            }
            updateInfo.append(title).append(value);
        }
    }
    /**
     * 拼接更新信息（字符串型）
     * @param updateInfo 更新信息
     * @param title 字段属性标题描述
     * @param value 更新前/后的信息
     * @author zcp
     * @date 2019/7/1 22:32
     * @since v1.0
     * @version v1.0
     */
    private void updateInfo4String(StringBuilder updateInfo, String title, String value) {
        //判空，非空时才拼接信息
        if (StringUtil.isNotEmpty(value)) {
            //非空时拼上分号
            if (updateInfo.length() > 0) {
                updateInfo.append(SymbolConstant.SEMICOLON_CHINESE).append(SymbolConstant.BR);
            }
            updateInfo.append(title).append(value);
        }
    }

    /**
     * 删除字段处理
     * @param explainList 解释list
     * @param name 字段中文名
     * @return com.xbongbong.pro.log.pojo.DetailLogPojo
     * @author zcp
     * @date 2019/6/30 22:40
     * @since v1.0
     * @version v1.0
     */
    private DetailLogPojo delAttr(List<FieldAttrEntity> explainList, String name) {
        int size = explainList.size();
        if (size == 0) {
            return null;
        }
        FieldAttrEntity oldOne = explainList.get(0);
        StringBuilder updataBefore = new StringBuilder();
        updataBefore.append(oldOne.getAttrName());
        StringBuilder other = new StringBuilder();
        if (size > 1) {
            other.append(I18nMessageUtil.getMessage(CommonConstant.ETC)).append(size).append(I18nMessageUtil.getMessage(I18nStringConstant.FIELDS));
        }
        updataBefore.append(other);
        String updataAfter = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, I18nMessageUtil.getMessage(I18nStringConstant.DISABLED_OR_DELETED));
        return new DetailLogPojo(BusinessConstant.PASS_FORM_EXPLAIN_ATTR_DEL, name, updataBefore.toString(), updataAfter);
    }

    /**
     * 判断字段值是否有变化
     * @param oldValue 旧字段的值
     * @param newValue 新字段的值
     * @return boolean true 更改，false 未更改
     * @author zcp
     * @date 2019/6/28 11:22
     * @since v1.0
     * @version v1.0
     */
    private boolean chage4Value(Object oldValue, Object newValue) {
        boolean change = false;
        if (newValue == null) {
            /**
             （1）当新实体内不存在该字段时，需判断旧字段是否也无值，如果旧的不为空，说明有更改
             旧数据的key存在，则value肯定不为null，所以要看其是否是空字符串
             */
            //旧的存在且不为空
            if (oldValue != null && !"".equals(oldValue)) {
                change = true;
            }
        } else {
            /**
             （2）当新实体内存在该字段时(newValue不为null)，直接比较新旧字段的值是否相等
             */
            if (!newValue.equals(oldValue)) {
                change = true;
            }
        }
        return change;
    }

    /**
     * 比较两个数组是否相等，不相等则代表更改了信息
     * @param oldValue 旧字段的值
     * @param newValue 新字段的值
     * @return boolean true 更改，false 未更改
     * @author zcp
     * @date 2019/6/29 17:23
     * @since v1.0
     * @version v1.0
     */
    private boolean chage4Array(Object oldValue, Object newValue) {
        JSONArray oldValueArray = oldValue == null ? null : JsonHelperUtil.parseArray(oldValue.toString());
        JSONArray newValueArray = newValue == null ? null : JsonHelperUtil.parseArray(newValue.toString());
        //相等则无更改，不相等则更改
        boolean change = !CompareUtil.compareArray(oldValueArray, newValueArray);
        return change;
    }

    /**
     * 比较两个对象是否相等，不相等则代表更改了信息
     * @param oldValue 旧字段的值
     * @param newValue 新字段的值
     * @return boolean true 更改，false 未更改
     * @author zcp
     * @date 2019/7/2 16:21
     * @since v1.0
     * @version v1.0
     */
    private boolean chage4Json(Object oldValue, Object newValue) {
        JSONObject jsonObjectOld = oldValue == null ? null : JsonHelperUtil.parseObject(oldValue.toString());
        JSONObject jsonObjectNew = newValue == null ? null : JsonHelperUtil.parseObject(newValue.toString());
        //相等则无更改，不相等则更改
        boolean change = !CompareUtil.compareJson(jsonObjectOld, jsonObjectNew);
        return change;
    }

    /**
     * 把Object类型转为string
     * @param value 要转为字符串的对象
     * @return java.lang.String
     * @author zcp
     * @date 2019/6/28 14:27
     * @version v1.0
     * @since v1.0
     */
    private String object2String(Object value) {
        return value == null ? null : value.toString();
    }

    /**
     * 把Object类型转为Long
     * @param value 要转为Long的对象
     * @return java.lang.Long
     * @author zcp
     * @date 2019/6/28 14:42
     * @since v1.0
     * @version v1.0
     */
    private Long object2Long(Object value) {
        Long result = 0L;
        if (Objects.isNull(value)) {
            return result;
        }
        try {
            result = Long.parseLong(value.toString());
        } catch (NumberFormatException e) {
        }
        return result;
    }
    /**
     * 把Object类型转为int
     * @param value 要转为int的对象
     * @param defaultValue 默认值
     * @return java.lang.Integer
     * @author zcp
     * @date 2019/6/28 17:04
     * @since v1.0
     * @version v1.0
     */
    private Integer object2Int(Object value, int defaultValue) {
        return value == null ? defaultValue : Integer.parseInt(value.toString());
    }

    /**
     * 把Object类型转为list合集
     * @param value 要转为list的对象
     * @return java.util.Set<T>
     * @author zcp
     * @date 2019/7/2 23:08
     * @since v1.0
     * @version v1.0
     */
    private List object2List(Object value) {
        if (value == null) {
            return new ArrayList<>();
        }
        String str;
        if (value instanceof List) {
            str = JSON.toJSONString(value);
        } else {
            str = value.toString();
        }
        return JsonHelperUtil.parseArray(str);
    }
    /**
     * 把Object类型转为set合集（对list排重）
     * @param value 要转为set的对象
     * @return java.util.Set<T>
     * @author zcp
     * @date 2019/7/2 23:08
     * @since v1.0
     * @version v1.0
     */
    private Set object2Set(Object value) {
        List list = object2List(value);
        return new HashSet<>(list);
    }

    /**
     * 拼接字段名，格式为：(attr_1)
     * @param updataStr 要拼接的StringBuilder
     * @param attr 被拼上的字段名（英文）
     * @return java.lang.StringBuilder
     * @author zcp
     * @date 2019/6/29 21:07
     * @since v1.0
     * @version v1.0
     */
    private StringBuilder appendAttr(StringBuilder updataStr, String attr) {
        return updataStr.append(String.format(SymbolConstant.BRACKETS_ENGLISH, attr));
    }

    /**
     * 格式化int型（0/1）为开启/关闭
     * @param value
     * @return java.lang.String
     * @author zcp
     * @date 2019/7/4 22:29
     * @since v1.0
     * @version v1.0
     */
    private String formatData4Enable(Object value){
        return formatData4Enable(value, I18nMessageUtil.getMessage(EnableEnum.OPEN.getMemo()), I18nMessageUtil.getMessage(EnableEnum.CLOSE.getMemo()));
    }

    /**
     * 格式化int型（0/1）为特定字符串（1为threeA，0为threeB）
     * @param value 0或1
     * @param threeA 当为1时显示该值
     * @param threeB 当为0时显示该值
     * @return java.lang.String
     * @author zcp
     * @date 2019/7/8 17:26
     * @since v1.0
     * @version v1.0
     */
    private String formatData4Enable(Object value, String threeA, String threeB) {
        Integer valueInt = object2Int(value, 0);
        return Objects.equals(BasicConstant.ONE, valueInt) ? threeA : threeB;
    }

    /**
     * 格式化数值范围，格式为：123 ~ 345
     * @param numericalLimitsPojo
     * @return java.lang.String
     * @author zcp
     * @date 2019/7/1 9:51
     * @since v1.0
     * @version v1.0
     */
    private String formatNumericalLimits(NumericalLimitsPoJo numericalLimitsPojo) {
        if (numericalLimitsPojo == null) {
            return "";
        }
        StringBuilder limits = new StringBuilder();
        limits.append(numericalLimitsPojo.getMin()).append(" ~ ").append(numericalLimitsPojo.getMax());
        return limits.toString();
    }

    /**
     * 格式化定位信息
     * @param locationCenter 定位设置list
     * @return java.lang.String
     * @author zcp
     * @date 2019/7/1 20:43
     * @since v1.0
     * @version v1.0
     */
    private String formatLocationCenter(List<LocationCenterPoJo> locationCenter) {
        if (locationCenter == null) {
            return "";
        }
        StringBuilder location = new StringBuilder();
        for (int i = 0; i < locationCenter.size(); i++) {
            LocationCenterPoJo item = locationCenter.get(i);
            if (i > 0) {
                location.append(SymbolConstant.SEMICOLON_CHINESE);
            }
            Integer index = i + 1;
            location.append(I18nMessageUtil.getMessage(CommonConstant.ADDRESS)).append(index).append(SymbolConstant.POINT_ENGLISH).append(item.getName()).append(SymbolConstant.SEMICOLON_ENGLISH);
            location.append(I18nMessageUtil.getMessage(I18nStringConstant.POSITIONING_RANGE)).append(item.getRadius()).append(I18nMessageUtil.getMessage(CommonConstant.METER));
        }
        return location.toString();
    }

    /**
     * 拼接成员多选的名称
     * @param userMap userId-员工实体
     * @param userIdSet 员工id合集
     * @return java.lang.String 员工姓名拼接的字符串，用逗号分隔，如“张三,李四”
     * @author zcp
     * @date 2019/7/2 23:24
     * @since v1.0
     * @version v1.0
     */
    private String formatUserNameInfo(Map<String, UserEntity> userMap, Collection<String> userIdSet) {
        StringBuilder userInfo = new StringBuilder();
        userIdSet.forEach(userId -> {
            UserEntity userBefore = userMap.get(userId);
            if (userBefore == null) {
                return;
            }
            if (userInfo.length() > 0) {
                userInfo.append(SymbolConstant.COMMA_ENGLISH);
            }
            userInfo.append(userBefore.getName());
        });
        return userInfo.toString();
    }

    /**
     * 拼接部门多选的名称
     * @param departmentMap 部门id-部门实体
     * @param depIdSet 部门id合集
     * @return java.lang.String 员工姓名拼接的字符串，用逗号分隔，如“张三,李四”
     * @author zcp
     * @date 2019/7/2 23:35
     * @since v1.0
     * @version v1.0
     */
    private String formatDepNameInfo(Map<String, DepartmentEntity> departmentMap, Collection<Long> depIdSet) {
        StringBuilder depInfo = new StringBuilder();
        for (Object object : depIdSet) {
            String idStr = String.valueOf(object);
            DepartmentEntity before = departmentMap.get(idStr);
            if (before == null) {
                continue;
            }
            if (depInfo.length() > 0) {
                depInfo.append(SymbolConstant.COMMA_ENGLISH);
            }
            depInfo.append(before.getName());
        }
        return depInfo.toString();
    }

    /**
     * 同时有部门名和员工名，需要拼接起来（工单模板授权人员，部门和员工平级展示）
     * @param userNames 员工名字符串
     * @param depNames 部门名字符串
     * @return java.lang.String
     * @author zcp
     * @date 2019/7/9 23:27
     * @since v1.0
     * @version v1.0
     */
    private String format4UserAndDep(String userNames, String depNames) {
        String updataInfo = "";
        if (StringUtil.isNotEmpty(userNames) && StringUtil.isNotEmpty(depNames)) {
            updataInfo = userNames + SymbolConstant.COMMA_CHINESE + depNames;
        } else if (StringUtil.isNotEmpty(userNames)) {
            updataInfo = userNames;
        } else if (StringUtil.isNotEmpty(depNames)) {
            updataInfo = depNames;
        }
        return updataInfo;
    }

    /**
     * 格式化工单详情内容（把名称、耗时、执行人员、派单规则拼起来）
     * @param workStageList 阶段信息
     * @return java.lang.String
     * @author zcp
     * @date 2019/7/11 13:58
     * @since v1.0
     * @version v1.0
     */
    private String formatWorkOrderTemplateStage(List<WorkOrderTemplateDetailWorkflowPojo> workStageList) {
        if (workStageList == null || workStageList.size() == 0) {
            return "";
        }
        StringBuilder sbName = new StringBuilder();
        sbName.append("【工单流程(耗时)】<br/>");
        StringBuilder sbUser = new StringBuilder();
        sbUser.append("<br/>【执行人员】<br/>");
        //后面拼上向右箭头
        for (WorkOrderTemplateDetailWorkflowPojo entity : workStageList) {
            //处理名称，拼上小时
            String time = String.format(SymbolConstant.BRACKETS_ENGLISH, entity.getExpectedTime() + I18nMessageUtil.getMessage(CommonConstant.HOUR));
            //拼箭头
            sbName.append(entity.getName()).append(time).append(SymbolConstant.ARROW_RIGHT);
            //处理各节点的人、部门、服务，拼上派单规则
            String refRobName = entity.getRefRobName();
            Integer refRobType = entity.getRefRobType();
            String refRobTypeName = I18nMessageUtil.getMessage(CommonConstant.TYPE) + "：" + WorkOrderStageRobTypeEnum.getNameByCode(refRobType);
            //如果不是员工，还需要拼上派单规则（员工的没有此规则）
            if (!Objects.equals(refRobType, WorkOrderStageRobTypeEnum.USER.getCode())) {
                String rule = WorkOrderStageRuleEnum.getNameByCode(entity.getRuleId());
                if (StringUtil.isNotEmpty(rule)) {
                    refRobTypeName = refRobTypeName + String.format(SymbolConstant.BRACKETS_SQUARE, "派单规则：" + rule);
                }
            }
            refRobTypeName = String.format(SymbolConstant.BRACKETS_ENGLISH, refRobTypeName);
            //拼箭头
            sbUser.append(refRobName).append(refRobTypeName).append(SymbolConstant.ARROW_RIGHT);
        }
        sbName.append("完成工单");
        sbUser.append("完成");
        return sbName.append(SymbolConstant.SEMICOLON_CHINESE).append(sbUser).toString();
    }

    /**
     * 封装“字段设置-可见/可编辑-高级设置-规则”内的配置
     * @param flag true可见 false可编辑
     * @param detailLogParamPojo 参数容器：员工、部门、角色
     * @param visibleRulePojo
     * @return java.lang.String
     * @author zcp
     * @date 2019/7/11 22:02
     * @since v1.0
     * @version v1.0
     */
    private String formatAttrRule(boolean flag, DetailLogParamPojo detailLogParamPojo, VisibleRulePoJo visibleRulePojo) {
        if (visibleRulePojo == null) {
            return "";
        }
        Integer type = visibleRulePojo.getType();
        //不给谁看、部分可见、或者未选择可见类型
        VisibleTypeEnum visibleTypeEnum = VisibleTypeEnum.getByType(type);
        String typeMemo = visibleTypeEnum.getMemo();
        if (!flag) {
            typeMemo = visibleTypeEnum.getEditMemo();
        }
        if (Objects.equals(visibleTypeEnum, VisibleTypeEnum.NONE)) {
            typeMemo = I18nMessageUtil.getMessage(I18nStringConstant.NO_VISIBLE_TYPE_SELECTED);
            if (!flag) {
                typeMemo = I18nMessageUtil.getMessage(I18nStringConstant.NO_EDITABLE_TYPE_SELECTED);
            }
        }
        StringBuilder sb = new StringBuilder();
        sb.append(typeMemo);
        //负责人、协同人、创建人
        String relative = RelatedPersonnelEnum.getRelatives(visibleRulePojo.getRelative());
        if (StringUtil.isNotEmpty(relative)) {
            sb.append(SymbolConstant.COLON_CHINESE).append(relative);
        }
        //授权角色
        List<Integer> role = visibleRulePojo.getRole();
        if (role != null && role.size() > 0) {
            sb.append(SymbolConstant.SEMICOLON_CHINESE).append(I18nMessageUtil.getMessage(CommonConstant.ROLE) + "：");
            Map<String, RoleEntity> roleMap = detailLogParamPojo.getRoleMap();
            for (int i = 0; i < role.size(); i++) {
                Integer value = role.get(i);
                RoleEntity entity = roleMap.get(value.toString());
                if (entity == null) {
                    continue;
                }
                if (i != 0) {
                    sb.append(SymbolConstant.COMMA_ENGLISH);
                }
                sb.append(entity.getRoleName());
            }
        }
        //授权部门
        List<VisibleRuleVO> depRuleVoList = visibleRulePojo.getDep();
        if (depRuleVoList != null && depRuleVoList.size() > 0) {
            sb.append(SymbolConstant.SEMICOLON_CHINESE).append(I18nMessageUtil.getMessage(CommonConstant.DEPARTMENT) + "：");
            Map<String, DepartmentEntity> departmentMap = detailLogParamPojo.getDepartmentMap();
            for (int i = 0; i < depRuleVoList.size(); i++) {
                VisibleRuleVO ruleVoDep = depRuleVoList.get(i);
                Object id = ruleVoDep.getId();
                if (id == null) {
                    continue;
                }
                DepartmentEntity entity = departmentMap.get(id.toString());
                if (entity == null) {
                    continue;
                }
                if (i != 0) {
                    sb.append(SymbolConstant.COMMA_ENGLISH);
                }
                sb.append(entity.getName());
            }
        }
        //授权员工
        List<String> userIdList = visibleRulePojo.getUser();
        if (userIdList != null && userIdList.size() > 0) {
            sb.append(SymbolConstant.SEMICOLON_CHINESE).append(I18nMessageUtil.getMessage(CommonConstant.EMPLOYEE) + "：");
            Map<String, UserEntity> userMap = detailLogParamPojo.getUserMap();
            for (int i = 0; i < userIdList.size(); i++) {
                String uid = userIdList.get(i);
                UserEntity entity = userMap.get(uid);
                if (entity == null) {
                    continue;
                }
                if (i != 0) {
                    sb.append(SymbolConstant.COMMA_ENGLISH);
                }
                sb.append(entity.getName());
            }
        }
        return sb.toString();
    }

    /**
     * 把VisibleRulePojo转为json对象，把内部无用的一些字段置空，如userList、roleList
     * @param visibleScopeRule
     * @return com.alibaba.fastjson.JSONObject
     * @author zcp
     * @date 2019/7/11 20:04
     * @since v1.0
     * @version v1.0
     */
    private JSONObject getJson4VisibleRule(VisibleRulePoJo visibleScopeRule) {
        JSONObject json = null;
        if (visibleScopeRule != null) {
            visibleScopeRule.setUserList(null);
            visibleScopeRule.setRoleList(null);
            json = JSONObject.parseObject(JSON.toJSONString(visibleScopeRule));
        }
        return json;
    }

    /**
     * 获取字段名
     * @param explainMap 字段解释map
     * @param attr 字段英文名
     * @param defaultName 字段默认名
     * @return java.lang.String
     * @author zcp
     * @date 2019/7/4 20:25
     * @since v1.0
     * @version v1.0
     */
    private String getAttrName(Map<String, FieldAttrEntity> explainMap, String attr, String defaultName) {
        String name = defaultName;
        FieldAttrEntity fieldAttrEntity = explainMap.get(attr);
        if (fieldAttrEntity != null) {
            name = fieldAttrEntity.getAttrName();
        }
        return name;
    }

    /**
     * 处理字段解释容器、部分数据字典容器
     * @param detailLogParamPojo 参数pojo
     * @param corpid 公司id
     * @param redundantTemplateTypeEnum 表单枚举
     * @param explainMap 字段名-字段实体
     * @return com.xbongbong.pro.log.pojo.DetailLogParamPojo
     * @throws XbbException
     * @author zcp
     * @date 2019/8/14 14:44
     * @update 2019/8/14 14:44
     * @since v1.0
     * @version v1.0
     */
    private DetailLogParamPojo setExplaninDataDictionary(DetailLogParamPojo detailLogParamPojo, String corpid, RedundantTemplateTypeEnum redundantTemplateTypeEnum, Map<String, FieldAttrEntity> explainMap) throws XbbException {
        //------explainMap
        detailLogParamPojo.setExplainMap(explainMap);

        //字段名-该字段的数据字典map（key为字典value，value为字典text）
        Map<String, Map<String, String>> specialDictionaryMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if (Objects.equals(redundantTemplateTypeEnum, RedundantTemplateTypeEnum.CUSTOMER)) {
            //客户状态
            String type = CustomerManagementEnum.TYPE.getAttr();
            if (explainMap.containsKey(type)) {
                Map<String, String> customerStatusMap = dataDictionaryModel.getDictionaryMap(DictionaryEnum.CUSTOMER_STATUS, corpid, null);
                specialDictionaryMap.put(type, customerStatusMap);
            }
        } else if (Objects.equals(redundantTemplateTypeEnum, RedundantTemplateTypeEnum.CONTRACT)) {
            //合同状态、物流公司
            String status = ContractEnum.STATUS.getAttr();
            String expressName = ContractEnum.EXPRESS_NAME.getAttr();
            if (explainMap.containsKey(status)) {
                Map<String, String> contractStatusMap = dataDictionaryModel.getDictionaryMap(DictionaryEnum.CONTRACT_STATUS, corpid, null);
                specialDictionaryMap.put(status, contractStatusMap);
            }
            if (explainMap.containsKey(expressName)) {
                Map<String, String> expressNameMap = dataDictionaryModel.getDictionaryMap(DictionaryEnum.EXPRESS_NAME, corpid, null);
                specialDictionaryMap.put(expressName, expressNameMap);
            }
        }
        detailLogParamPojo.setSpecialDictionaryMap(specialDictionaryMap);
        return detailLogParamPojo;
    }
    /**
     * 封装判断业务日志时需要的基础数据容器，如员工、部门、解释等
     * @param corpid 公司id
     * @param redundantTemplateTypeEnum 表单枚举
     * @param explainMap 字段名-字段实体
     * @return com.xbongbong.pro.log.pojo.DetailLogDataPojo
     * @throws XbbException
     * @author zcp
     * @date 2019/7/5 21:27
     * @since v1.0
     * @version v1.0
     */
    private DetailLogParamPojo getDetailLogDataPojo(String corpid, RedundantTemplateTypeEnum redundantTemplateTypeEnum, Map<String, FieldAttrEntity> explainMap) throws XbbException {
        //------渲染数据需要的一些数据塞入detailLogDataPojo
        DetailLogParamPojo detailLogParamPojo = new DetailLogParamPojo();

        // ---- 获取用户和部门容器userMap、departmentMap
        UserAndDepartmentGetDTO userAndDepartmentGetDTO = new UserAndDepartmentGetDTO();
        userAndDepartmentGetDTO.setCorpid(corpid);
        UserAndDepartmentGetVO userAndDepartmentGetVO = commonHelp.getUserDepartmentMap(userAndDepartmentGetDTO,BasicConstant.ZERO);
        Map<String, UserEntity> userMap = userAndDepartmentGetVO.getUserMap();
        Map<String, DepartmentEntity> departmentMap = userAndDepartmentGetVO.getDepartmentMap();
        detailLogParamPojo.setUserMap(userMap);
        detailLogParamPojo.setDepartmentMap(departmentMap);

        //------explainMap
        return detailLogParamPojo;
    }

    /**
     * 获取操作类型
     */
    public OperateModuleTypeEnum getOperateModuleTypeEnum(XbbRefTypeEnum xbbRefTypeEnum) {
        OperateModuleTypeEnum operateModuleTypeEnum;
        switch (xbbRefTypeEnum) {
            case CUSTOMER_MANAGEMENT:
                operateModuleTypeEnum = OperateModuleTypeEnum.CUSTOMER;
                break;
            case CONTRACT:
                operateModuleTypeEnum = OperateModuleTypeEnum.CONTRACT;
                break;
            case CONTACT:
                operateModuleTypeEnum = OperateModuleTypeEnum.CONTACT;
                break;
            case REFUND:
                operateModuleTypeEnum = OperateModuleTypeEnum.REFUND;
                break;
            case OUTSTOCK:
            case OTHER_OUTSTOCK:
                operateModuleTypeEnum = OperateModuleTypeEnum.OUTSTOCK;
                break;
            case CONTRACT_OUTSTOCK:
                operateModuleTypeEnum = OperateModuleTypeEnum.CONTRACT_OUTSTOCK;
                break;
            case RETURNED_PURCHASE_OUTSTOCK:
                operateModuleTypeEnum = OperateModuleTypeEnum.RETURNED_PURCHASE_OUTSTOCK;
                break;
            case WORK_ORDER_OUTSTOCK:
                operateModuleTypeEnum = OperateModuleTypeEnum.WORK_ORDER_OUTSTOCK;
                break;
            case PRODUCTION_MATERIEL_OUTSTOCK:
                operateModuleTypeEnum = OperateModuleTypeEnum.PRODUCTION_MATERIEL_OUTSTOCK;
                break;
            case RETURNED_PURCHASE:
                operateModuleTypeEnum = OperateModuleTypeEnum.RETURN_PURCHASE;
                break;
            case SALES_OPPORTUNITY:
                operateModuleTypeEnum = OperateModuleTypeEnum.OPPORTUNITY;
                break;
            case BOM_BILL:
                operateModuleTypeEnum = OperateModuleTypeEnum.BOMBILL;
                break;
            case CUSTOMER_COMMUNICATE:
                operateModuleTypeEnum = OperateModuleTypeEnum.CustomerCommunicate;
                break;
            case SUPPLIER_COMMUNICATE:
                operateModuleTypeEnum = OperateModuleTypeEnum.SUPPLIER_COMMUNICATE;
                break;
            case WORKREPORT_DAILY:
            case WORKREPORT_WEEKLY:
            case WORKREPORT_MONTHLY:
            case WORK_REPORT:
                operateModuleTypeEnum = OperateModuleTypeEnum.WORKREPORT;
                break;
            case COMMUNICATE_PLAN:
                operateModuleTypeEnum = OperateModuleTypeEnum.CommunicatePlan;
                break;
            case SUPPLIER:
                operateModuleTypeEnum = OperateModuleTypeEnum.SUPPLIER;
                break;
            case SUPPLIER_CONTACT:
                operateModuleTypeEnum = OperateModuleTypeEnum.SUPPLIER_CONTACT;
                break;
            case PURCHASE:
                operateModuleTypeEnum = OperateModuleTypeEnum.PURCHASE;
                break;
            case PRODUCTION_ORDER:
                operateModuleTypeEnum = OperateModuleTypeEnum.PRODUCTION_ORDER;
                break;
            case PAYMENT:
                operateModuleTypeEnum = OperateModuleTypeEnum.PaymentManage;
                break;
            case PAY_PLAN:
                operateModuleTypeEnum = OperateModuleTypeEnum.PayManage;
                break;
            case INVOICE:
                operateModuleTypeEnum = OperateModuleTypeEnum.INVOICE;
                break;
            case PURCHASE_INVOICE:
                operateModuleTypeEnum = OperateModuleTypeEnum.PURCHASE_INVOICE;
                break;
            case INVENTORY:
                operateModuleTypeEnum = OperateModuleTypeEnum.INVENTORY;
                break;
            case TRANSFER:
                operateModuleTypeEnum = OperateModuleTypeEnum.TRANSFER;
                break;
            case WAREHOUSE:
                operateModuleTypeEnum = OperateModuleTypeEnum.WAREHOUSE;
                break;
            case PRODUCT:
                operateModuleTypeEnum = OperateModuleTypeEnum.PRODUCT;
                break;
            case ASSEMBLE:
                operateModuleTypeEnum = OperateModuleTypeEnum.ASSEMBLE;
                break;
            case INSTOCK:
            case OTHER_INSTOCK:
                operateModuleTypeEnum = OperateModuleTypeEnum.INSTOCK;
                break;
            case PURCHASE_INSTOCK:
                operateModuleTypeEnum = OperateModuleTypeEnum.PURCHASE_INSTOCK;
                break;
            case REFUND_INSTOCK:
                operateModuleTypeEnum = OperateModuleTypeEnum.REFUND_INSTOCK;
                break;
            case PRODUCTION_INSTOCK:
                operateModuleTypeEnum = OperateModuleTypeEnum.PRODUCTION_INSTOCK;
                break;
            case RETURNED_MATERIEL_INSTOCK:
                operateModuleTypeEnum = OperateModuleTypeEnum.RETURNED_MATERIEL_INSTOCK;
                break;
            case QUOTATION:
                operateModuleTypeEnum = OperateModuleTypeEnum.QUOTATION;
                break;
            case ORI_STOCK:
                operateModuleTypeEnum = OperateModuleTypeEnum.ORI_STOCK;
                break;
            case PAYMENT_SHEET:
                operateModuleTypeEnum = OperateModuleTypeEnum.PAYMENT_SHEET;
                break;
            case PAY_SHEET:
                operateModuleTypeEnum = OperateModuleTypeEnum.PAY_SHEET;
                break;
            case MARKET_ACTIVITY:
                operateModuleTypeEnum = OperateModuleTypeEnum.MARKET_ACTIVITY;
                break;
            case CLUE:
                operateModuleTypeEnum = OperateModuleTypeEnum.CLUE;
                break;
            case DISTRIBUTOR_MANAGEMENT:
                operateModuleTypeEnum = OperateModuleTypeEnum.DISTRIBUTOR;
                break;
            case DISTRIBUTOR_CONTACT:
                operateModuleTypeEnum = OperateModuleTypeEnum.DISTRIBUTOR_CONTACT;
                break;
            case DISTRIBUTOR_COMMUNICATE:
                operateModuleTypeEnum = OperateModuleTypeEnum.DISTRIBUTOR_COMMUNICATE;
                break;
            case DISTRIBUTOR_ACCOUNT:
                operateModuleTypeEnum = OperateModuleTypeEnum.DISTRIBUTOR_ACCOUNT;
                break;
            case ORDER:
                operateModuleTypeEnum = OperateModuleTypeEnum.ORDER;
                break;
            case RETURN:
                operateModuleTypeEnum = OperateModuleTypeEnum.RETURN;
                break;
            case ORDER_OUTSTOCK:
                operateModuleTypeEnum = OperateModuleTypeEnum.ORDER_OUTSTOCK;
                break;
            case FUND_TRANSFER:
                operateModuleTypeEnum = OperateModuleTypeEnum.FUND_TRANSFER;
                break;
            case OTHER_INCOME:
            case OTHER_EXPENSE:
                operateModuleTypeEnum = OperateModuleTypeEnum.OTHER_FUND_MANAGEMENT;
                break;
            case COST_ADJUST:
                operateModuleTypeEnum = OperateModuleTypeEnum.COST_ADJUST;
                break;
            case KNOWLEDGE_BASE:
                operateModuleTypeEnum = OperateModuleTypeEnum.KNOWLEDGE_BASE;
                break;
            case COMPETITOR:
                operateModuleTypeEnum = OperateModuleTypeEnum.COMPETITOR;
                break;
            case SERVICE_PROJECT:
                operateModuleTypeEnum = OperateModuleTypeEnum.SERVICE_PROJECT;
                break;
            case WORK_ORDER_V2:
                operateModuleTypeEnum = OperateModuleTypeEnum.WORK_ORDER_V2;
                break;
            case RECEIPT_ORDER:
                operateModuleTypeEnum = OperateModuleTypeEnum.RECEIPT_ORDER;
                break;
            default:
                operateModuleTypeEnum = OperateModuleTypeEnum.UNKNOWN;
        }
        return operateModuleTypeEnum;
    }

    /**
     * 获取智能助手执行时间
     * 
     * @param oldStrategyPojo 旧对象
     * @param newStrategyPojo 新对象
     * @throws 
     * @return 
     * @author hongxiao
     * @date 2019-12-13 16:56
     * @since
     * @version
     */
    private DetailLogPojo getAiStrategyTimeInfo(SmartStrategyPojo oldStrategyPojo, SmartStrategyPojo newStrategyPojo) {

        // 执行策略类型
        StrategyTypeEnum oldTypeEnum = StrategyTypeEnum.getByCode(oldStrategyPojo.getStrategyType());
        StrategyTypeEnum newTypeEnum = StrategyTypeEnum.getByCode(newStrategyPojo.getStrategyType());
        // 执行时间
        String oldStrategyTime = getAiStrategyTime(oldStrategyPojo, oldTypeEnum);
        String newStrategyTime = getAiStrategyTime(newStrategyPojo, newTypeEnum);

        DetailLogPojo timeLogPojo = log4Normal("strategyTime", I18nMessageUtil.getMessage(I18nStringConstant.EXECUTION_TIME), oldStrategyTime, newStrategyTime);
        return timeLogPojo;
    }

    /**
     * 获取智能助手执行时间
     *
     * @param strategyPojo 执行策略实体
     * @param typeEnum 执行策略类型
     * @throws
     * @return
     * @author hongxiao
     * @date 2019-12-13 16:58
     * @since v1.0
     * @version v1.0
     */
    private String getAiStrategyTime(SmartStrategyPojo strategyPojo, StrategyTypeEnum typeEnum) {
        StringBuffer imeBuffer = new StringBuffer();
        switch (typeEnum) {
            // 只执行一次：执行时间
            // 每天：执行时间
            case ONLY_ONE:
            case DAILY:
                imeBuffer.append(DAILY.getMemo()).append(strategyPojo.getStrategyTime());
                break;
            // 每周：执行星期，执行时间，执行次数
            case WEEKLY:
                WeekEnum weekEnum = WeekEnum.getByCode(strategyPojo.getStrategyWeek());
                imeBuffer.append(WEEKLY.getMemo()).append(weekEnum.getName()).append(" ").append(strategyPojo.getStrategyTime());
                break;
            // 每月：执行日期，执行时间，执行次数
            case MONTHLY:
                imeBuffer.append(MONTHLY.getMemo()).append(strategyPojo.getStrategyDay()).append(I18nMessageUtil.getMessage(CommonConstant.NUMBER)).append(strategyPojo.getStrategyTime());
                break;
            // 每年：执行月份，执行天数，执行时间，执行次数
            case ANNUALLY:
                imeBuffer.append(ANNUALLY.getMemo()).append(strategyPojo.getStrategyDay()).append(" ").append(strategyPojo.getStrategyTime());
                break;
            default:
                break;
        }

        return imeBuffer.toString();
    }

    /**
     * 获取触发条件的完整拼接语句（字段＋筛选条件＋筛选值）
     *
     * @param triggerCondition 触发条件
     * @throws
     * @return
     * @author hongxiao
     * @date 2019-12-16 09:40
     * @since v1.0
     * @version v1.0
     */
    private String getAiTriggerCondition(List<AiTriggerConditionLogPojo> triggerCondition) {
        if (Objects.isNull(triggerCondition) || triggerCondition.isEmpty()) {
            return "";
        }
        StringBuffer triggerBuffer = new StringBuffer();
        List<String> waitDayFieldList = Arrays.asList(SmartSettingsFieldEnum.WAIT_PURCHASE.getAlias(),
                SmartSettingsFieldEnum.WAIT_PRODUCTION_ORDER.getAlias(), SmartSettingsFieldEnum.WAIT_INSTOCK.getAlias(),
                SmartSettingsFieldEnum.WAIT_OUTSTOCK.getAlias());
        List<String> dayFileldList = Arrays.asList(FieldTypeEnum.DATETIME.getAlias(), FieldTypeEnum.ADDTIME.getAlias(),
                FieldTypeEnum.UPDATETIME.getAlias(), SmartSettingsFieldEnum.FORGET_CUSTOMER.getAlias(), SmartSettingsFieldEnum.FORGET_OPPORTUNITY.getAlias(),
                SmartSettingsFieldEnum.FORGET_SUPPLIER.getAlias(), SmartSettingsFieldEnum.PRODUCT_EXPIRE_WARNING.getAlias(),
                SmartSettingsFieldEnum.CONTRACT_DEAD_LINE.getAlias(), SmartSettingsFieldEnum.PURCHASE_ARRIVE_TIME.getAlias(),
                SmartSettingsFieldEnum.PRODUCTION_ORDER_PLAN_TIME.getAlias(), SmartSettingsFieldEnum.PAYMENT_DEAD_LINE.getAlias(),
                SmartSettingsFieldEnum.PAYMENT_OVERDUE.getAlias(), SmartSettingsFieldEnum.PAY_PLAN_DEAD_LINE.getAlias(),
                SmartSettingsFieldEnum.PAY_PLAN_OVERDUE.getAlias());

        for (AiTriggerConditionLogPojo pojo : triggerCondition) {
            triggerBuffer.append("“").append(pojo.getAttrName()).append("”");
            triggerBuffer.append("  ").append(pojo.getSymbolName());
            // 范围：～，其他：、，是否需要加天
            // 340107, 340108, 340109, 340110：天不处理
            String suffix = "";
            if (waitDayFieldList.contains(pojo.getAttr()) && !pojo.getSymbol().equals(SmartSettingsConditionEnum.NOW)) {
                suffix = "天处理";
            } else if (dayFileldList.contains(pojo.getAttr()) && !pojo.getSymbol().equals(SmartSettingsConditionEnum.NOW)) {
                suffix = I18nMessageUtil.getMessage(CommonConstant.DAY);
            }
            String separator = pojo.getSymbol().equals(ConditionEnum.RANGE.getSymbol()) ? "～" : "、";

            if (Objects.nonNull(pojo.getValue()) && !pojo.getValue().isEmpty()) {
                StringBuffer valueBuffer = new StringBuffer();
                for (Object value : pojo.getValue()) {
                    if (valueBuffer.length() == BasicConstant.ZERO) {
                        valueBuffer.append(value);
                    } else {
                        valueBuffer.append(separator).append(value);
                    }
                }
                triggerBuffer.append("  ").append(valueBuffer).append(suffix);
            }
            triggerBuffer.append(";<br/>");
        }
        return triggerBuffer.toString();
    }

    /**
     * 获取触发条件的完整拼接语句（字段＋筛选条件＋筛选值）
     *
     * @param triggerCondition 触发条件
     * @throws
     * @return
     * @author hongxiao
     * @date 2019-12-16 09:40
     * @since v1.0
     * @version v1.0
     */
    private String getDwTriggerCondition(JSONArray triggerCondition) {
        List<DwTriggerConditionPojo> triggerConditionPojoList = JSONArray.parseArray(triggerCondition.toJSONString(),DwTriggerConditionPojo.class);
        if (Objects.isNull(triggerCondition) || triggerCondition.isEmpty()) {
            return "";
        }
        StringBuffer triggerBuffer = new StringBuffer();

        for (DwTriggerConditionPojo pojo : triggerConditionPojoList) {
            triggerBuffer.append("“").append(pojo.getAttrName()).append("”");
            triggerBuffer.append("  ").append(pojo.getSymbolName());
            // 范围：～，其他：、，是否需要加天
            String suffix = "";
            String separator = pojo.getSymbol().equals(ConditionEnum.RANGE.getSymbol()) ? "～" : "、";
            if (Objects.nonNull(pojo.getValue()) && !pojo.getValue().isEmpty()) {
                StringBuffer valueBuffer = new StringBuffer();
                for (Object value : pojo.getValue()) {
                    if (valueBuffer.length() == BasicConstant.ZERO) {
                        valueBuffer.append(value);
                    } else {
                        valueBuffer.append(separator).append(value);
                    }
                }
                triggerBuffer.append("  ").append(valueBuffer).append(suffix);
            }
            triggerBuffer.append(";<br/>");
        }
        return triggerBuffer.toString();
    }

    /**
     * 解析
     * @param corpid
     * @param fieldType
     * @param isFieldDependence
     * @param logFieldEditedList
     * @return
     * @throws XbbException
     */
    public void analysisLabel(String corpid, Integer fieldType, Boolean isFieldDependence, List<AttrValuePojo> logFieldEditedList) throws XbbException {
        if (isFieldDependence) {
            return;
        }
        AttrValuePojo attrValuePojo = logFieldEditedList.get(0);
        if(Objects.equals(fieldType, FieldTypeEnum.LABEL.getType())){
            String logValue = (String)attrValuePojo.getValue();
            if (Objects.isNull(logValue)) {
                return;
            }
            List<String> labelList = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            // stringToLongList
            if (logValue!="[]") {
                logValue = logValue.replace("[","").replace("]","");
                List<Long> labelIdIn = Arrays.stream(logValue.split(",")).map(s -> Long.parseLong(s.trim())).collect(Collectors.toList());
                List<LabelEntity> labelEntityList = labelModel.getByLabelIds(labelIdIn, corpid);
                labelEntityList.forEach(labelEntity -> labelList.add(labelEntity.getName()));
                logValue = StringUtils.join(labelList, "，");
            } else {
                logValue ="";
            }
            attrValuePojo.setValue(logValue);
        }
    }

    /**
     * 工作报告设置日志
     *
     * @param oldObj 旧设置实体
     * @param newObj 新设置实体
     *
     * @throws
     * @return
     * @author hongxiao
     * @date 2021-03-16 10:40
     * @since
     * @version
     */
    private List<DetailLogPojo> detailLog4WorkReportSet(JSONObject oldObj, JSONObject newObj) {
        List<DetailLogPojo> detailArr = new ArrayList<>();
        //true 未更改，不需插入详情日志 false 有更改，需插入详情日志
        boolean flag = true;
        // 工作报告类型名称
        WorkReportTypeEnum workReportTypeEnum = WorkReportTypeEnum.getByType(oldObj.getInteger("type"));
        String reportName = workReportTypeEnum.getName();

        for (Map.Entry<String, Object> entry : oldObj.entrySet()) {
            String key = entry.getKey();
            Object valueOld = entry.getValue();
            Object valueNew = newObj.get(key);
            // 工作报告使用范围、发送人所属部门＆批阅人＆抄送人、统计指标、截至时间（日期、时间）、补发（补发标识，补发时长）、启用
            if ("visibleObject".equals(key)) {
                if (chage4Array(valueOld, valueNew)) {
                    flag = false;
                    List<OptionalRangeEntity> oldExecutorList = JsonHelperUtil.parseArray(valueOld.toString(), OptionalRangeEntity.class);
                    List<OptionalRangeEntity> newExecutorList = JsonHelperUtil.parseArray(valueNew.toString(), OptionalRangeEntity.class);
                    StringBuffer oldNameBuffer = new StringBuffer();
                    StringBuffer newNameBuffer = new StringBuffer();
                    for (OptionalRangeEntity entity : oldExecutorList) {
                        if (oldNameBuffer.length() == BasicConstant.ZERO) {
                            oldNameBuffer.append(entity.getName());
                        } else {
                            oldNameBuffer.append("、" + entity.getName());
                        }
                    }
                    for (OptionalRangeEntity entity : newExecutorList) {
                        if (newNameBuffer.length() == BasicConstant.ZERO) {
                            newNameBuffer.append(entity.getName());
                        } else {
                            newNameBuffer.append("、" + entity.getName());
                        }
                    }
                    DetailLogPojo detailLogPojo = log4Normal(key, reportName + I18nMessageUtil.getMessage(CommonConstant.RANGE_OF_USE), oldNameBuffer.toString(), newNameBuffer.toString());
                    detailArr.add(detailLogPojo);
                }
            } else if ("ccUser".equals(key)) {
                if (chage4Array(valueOld, valueNew)) {

                    List<WorkReportSetUserCcPojo> ccUserListOld = JsonHelperUtil.parseArray(valueOld.toString(), WorkReportSetUserCcPojo.class);
                    List<WorkReportSetUserCcPojo> ccUserListNew = JsonHelperUtil.parseArray(valueNew.toString(), WorkReportSetUserCcPojo.class);
                    int count = Math.max(ccUserListOld.size(), ccUserListNew.size());
                    int oldCount = ccUserListOld.size();
                    int newCount = ccUserListNew.size();

                    for (int i =0; i < count; i++) {
                        WorkReportSetUserCcPojo ccUserOld = null;
                        WorkReportSetUserCcPojo ccUserNew = null;
                        if (i < oldCount) {
                            ccUserOld = ccUserListOld.get(i);
                        }
                        if (i < newCount) {
                            ccUserNew = ccUserListNew.get(i);
                        }
                        if (Objects.equals(ccUserOld, ccUserNew)) {
                            continue;
                        }


                        // 每一组的所属部门、批阅人、抄送人比较
                        List<IdNamePropertyPojo> depsListOld = null;
                        List<IdNamePropertyPojo> userListOld = null;
                        List<IdNamePropertyPojo> ccListOld = null;

                        List<IdNamePropertyPojo> depsListNew = null;
                        List<IdNamePropertyPojo> userListNew = null;
                        List<IdNamePropertyPojo> ccListNew = null;

                        if (Objects.nonNull(ccUserOld)) {
                            depsListOld = ccUserOld.getEffectiveDeps();
                            userListOld = ccUserOld.getApprovedPeople();
                            ccListOld = ccUserOld.getUsers();
                        }
                        if (Objects.nonNull(ccUserNew)) {
                            depsListNew = ccUserNew.getEffectiveDeps();
                            userListNew = ccUserNew.getApprovedPeople();
                            ccListNew = ccUserNew.getUsers();
                        }

                        String depsStrOld = workReportSetUserCcLog(depsListOld);
                        String depsStrNew = workReportSetUserCcLog(depsListNew);
                        String userStrOld = workReportSetUserCcLog(userListOld);
                        String userStrNew = workReportSetUserCcLog(userListNew);
                        String ccStrOld = workReportSetUserCcLog(ccListOld);
                        String ccStrNew = workReportSetUserCcLog(ccListNew);

                        // 比较
                        if (chage4Value(depsStrOld, depsStrNew)) {
                            flag = false;

                            DetailLogPojo detailLogPojo = log4Normal(key, PaasConstant.LEFT_BRACKET  + (i+1) + PaasConstant.RIGHT_BRACKET +  I18nMessageUtil.getMessage(I18nStringConstant.DEPARTMENT) , depsStrOld, depsStrNew);
                            detailArr.add(detailLogPojo);
                        }
                        if (chage4Value(userStrOld, userStrNew)) {
                            flag = false;

                            DetailLogPojo detailLogPojo = log4Normal(key, PaasConstant.LEFT_BRACKET  + (i+1) + PaasConstant.RIGHT_BRACKET +  I18nMessageUtil.getMessage(AllFieldEnum.APPROVED_PEOPLE.getAttrName()) , userStrOld, userStrNew);
                            detailArr.add(detailLogPojo);
                        }
                        if (chage4Value(ccStrOld, ccStrNew)) {
                            flag = false;

                            DetailLogPojo detailLogPojo = log4Normal(key, PaasConstant.LEFT_BRACKET  + (i+1) + PaasConstant.RIGHT_BRACKET +  I18nMessageUtil.getMessage(CommonConstant.COPYER) , ccStrOld, ccStrNew);
                            detailArr.add(detailLogPojo);
                        }

                    }
                }
            } else if ("statisticOption".equals(key)) {
                if (chage4Json(valueOld, valueNew)) {
                    flag = false;
                    StatisticOptionPojo statisticPojoOld = JsonHelperUtil.parseObject(valueOld.toString(), StatisticOptionPojo.class);
                    StatisticOptionPojo statisticPojoNew = JsonHelperUtil.parseObject(valueNew.toString(), StatisticOptionPojo.class);
                    StringBuffer sbBefore = new StringBuffer();
                    StringBuffer sbAfter = new StringBuffer();
                    // 新增客户数
                    if (chage4Value(statisticPojoOld.getCustomerAddCount(), statisticPojoNew.getCustomerAddCount())) {
                        joinLogEnable(sbBefore, I18nMessageUtil.getMessage(I18nStringConstant.ADD_CUSTOMER), formatData4Enable(statisticPojoOld.getCustomerAddCount()));
                        joinLogEnable(sbAfter, I18nMessageUtil.getMessage(I18nStringConstant.ADD_CUSTOMER), formatData4Enable(statisticPojoNew.getCustomerAddCount()));
                    }
                    // 新增跟进记录数
                    if (chage4Value(statisticPojoOld.getCommunicateAddCount(), statisticPojoNew.getCommunicateAddCount())) {
                        joinLogEnable(sbBefore, I18nMessageUtil.getMessage(I18nStringConstant.ADD_COMMUNICATE), formatData4Enable(statisticPojoOld.getCommunicateAddCount()));
                        joinLogEnable(sbAfter, I18nMessageUtil.getMessage(I18nStringConstant.ADD_COMMUNICATE), formatData4Enable(statisticPojoNew.getCommunicateAddCount()));
                    }
                    // 新增机会数
                    if (chage4Value(statisticPojoOld.getOpportunityAddCount(), statisticPojoNew.getOpportunityAddCount())) {
                        joinLogEnable(sbBefore, I18nMessageUtil.getMessage(I18nStringConstant.ADD_OPPORTUNITY), formatData4Enable(statisticPojoOld.getOpportunityAddCount()));
                        joinLogEnable(sbAfter, I18nMessageUtil.getMessage(I18nStringConstant.ADD_OPPORTUNITY), formatData4Enable(statisticPojoNew.getOpportunityAddCount()));
                    }
                    // 新增机会金额
                    if (chage4Value(statisticPojoOld.getOppAmount(), statisticPojoNew.getOppAmount())) {
                        joinLogEnable(sbBefore, I18nMessageUtil.getMessage(I18nStringConstant.ADD_OPPORTUNITY_AMOUNT), formatData4Enable(statisticPojoOld.getOppAmount()));
                        joinLogEnable(sbAfter, I18nMessageUtil.getMessage(I18nStringConstant.ADD_OPPORTUNITY_AMOUNT), formatData4Enable(statisticPojoNew.getOppAmount()));
                    }
                    // 新增合同数
                    if (chage4Value(statisticPojoOld.getContractAddCount(), statisticPojoNew.getContractAddCount())) {
                        joinLogEnable(sbBefore, I18nMessageUtil.getMessage(I18nStringConstant.ADD_CONTRACT), formatData4Enable(statisticPojoOld.getContractAddCount()));
                        joinLogEnable(sbAfter, I18nMessageUtil.getMessage(I18nStringConstant.ADD_CONTRACT), formatData4Enable(statisticPojoNew.getContractAddCount()));
                    }
                    // 新增合同金额
                    if (chage4Value(statisticPojoOld.getContractAmount(), statisticPojoNew.getContractAmount())) {
                        joinLogEnable(sbBefore, I18nMessageUtil.getMessage(I18nStringConstant.ADD_CONTRACT_AMOUNT), formatData4Enable(statisticPojoOld.getContractAmount()));
                        joinLogEnable(sbAfter, I18nMessageUtil.getMessage(I18nStringConstant.ADD_CONTRACT_AMOUNT), formatData4Enable(statisticPojoNew.getContractAmount()));
                    }
                    // 新增回款金额
                    if (chage4Value(statisticPojoOld.getPaymentSheetAmount(), statisticPojoNew.getPaymentSheetAmount())) {
                        joinLogEnable(sbBefore, I18nMessageUtil.getMessage(I18nStringConstant.ADD_PAYMENT_AMOUNT), formatData4Enable(statisticPojoOld.getPaymentSheetAmount()));
                        joinLogEnable(sbAfter, I18nMessageUtil.getMessage(I18nStringConstant.ADD_PAYMENT_AMOUNT), formatData4Enable(statisticPojoNew.getPaymentSheetAmount()));
                    }
                    // 去掉最后一个字符
                    DetailLogPojo detailLogPojo = log4Normal(key, reportName + I18nMessageUtil.getMessage(CommonConstant.STATISTIC_TARGET), sbBefore.substring(0, sbAfter.length() -1), sbAfter.substring(0, sbAfter.length() -1));
                    detailArr.add(detailLogPojo);
                }
            }else if ("deadlineOption".equals(key)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    WorkReportDeadDayTypeEnum dayTypeEnumOld = WorkReportDeadDayTypeEnum.getByCodeType(Integer.valueOf(valueOld.toString()), workReportTypeEnum.getType());
                    WorkReportDeadDayTypeEnum dayTypeEnumNew = WorkReportDeadDayTypeEnum.getByCodeType(Integer.valueOf(valueNew.toString()), workReportTypeEnum.getType());

                    DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(CommonConstant.CLOSING_DATE), dayTypeEnumOld.getName(), dayTypeEnumNew.getName());
                    detailArr.add(detailLogPojo);
                }
            } else if ("deadlineTime".equals(key)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(CommonConstant.DEAD_LINE), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("reissue".equals(key)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Enable(key, I18nMessageUtil.getMessage(CommonConstant.REISSUE) + reportName, valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("reissueTimeLimit".equals(key)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    String day = I18nMessageUtil.getMessage(CommonConstant.DAY);
                    DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(CommonConstant.REISSUE_TIME_LIMIT), valueOld + day, valueNew + day);
                    detailArr.add(detailLogPojo);
                }
            } else if ("enable".equals(key)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Enable(key, reportName, valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            }
        }
        if (flag) {
            return null;
        }
        return detailArr;
    }

    /**
     * 多个开关数据拉普拼接
     *
     * @param sb 结果数据流
     * @param name 开关名称
     * @param value 开关值
     */
    private void joinLogEnable(StringBuffer sb, String name, String value) {
        sb.append(name)
                .append(PaasConstant.LEFT_BRACKET)
                .append(value)
                .append(PaasConstant.RIGHT_BRACKET).append("、");
    }


    /**
     * 拼接批阅人和抄送人
     *
     * @param list 列表
     * @return
     */
    private String workReportSetUserCcLog(List<IdNamePropertyPojo> list) {
        String resultStr = "";

        if (Objects.nonNull(list) && !list.isEmpty()) {
            StringBuffer sb = new StringBuffer();
            for (IdNamePropertyPojo pojo : list) {
                sb.append(pojo.getName()).append(SymbolConstant.COMMA_ENGLISH);
            }
            resultStr = sb.substring(0, sb.length() -1);
        }
        return resultStr;
    }

/**
 * 数据预警日志详情
 *
 * @param corpid 公司id
 * @param oldObj 旧对象
 * @param newObj 新对象
 * @throws
 * @return
 * @author hongxiao
 * @date 2019-12-13 16:21
 * @since v1.0
 * @version v1.0
 */
public List<DetailLogPojo> detailLog4DataWarning(String corpid, JSONObject oldObj, JSONObject newObj, JSONArray oldTriggerCondition, JSONArray newTriggerCondition) {
    List<DetailLogPojo> detailArr = new ArrayList<>();
    //true 未更改，不需插入详情日志 false 有更改，需插入详情日志
    boolean flag = true;
    for (Map.Entry<String, Object> entry : oldObj.entrySet()) {
        String key = entry.getKey();
        Object valueOld = entry.getValue();
        Object valueNew = newObj.get(key);
        if ("name".equals(key)) {
            if (chage4Value(valueOld, valueNew)) {
                flag = false;
                DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(I18nStringConstant.DATA_WARNING_NAME), valueOld, valueNew);
                detailArr.add(detailLogPojo);
            }
        } else if ("historyMaxNum".equals(key)) {
            if (chage4Value(valueOld, valueNew)) {
                flag = false;
                DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(I18nStringConstant.DATA_WARNING_HISTORY_MAX_NUM), valueOld, valueNew);
                detailArr.add(detailLogPojo);
            }
        } else if ("triggerCondition".equals(key)) {
            if (chage4Array(valueOld, valueNew)) {
                flag = false;
                String oldTriggerStr = getDwTriggerCondition(oldTriggerCondition);
                String newTriggerStr = getDwTriggerCondition(newTriggerCondition);

                DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(I18nStringConstant.TRIGGERING_CONDITIONS), oldTriggerStr, newTriggerStr);
                detailArr.add(detailLogPojo);
            }
        } else if ("action".equals(key)) {
            if (chage4Value(valueOld, valueNew)) {
                flag = false;
                SmartActionTypeEnum oldTypeEnum = SmartActionTypeEnum.getByCode(Integer.valueOf(valueOld.toString()));
                SmartActionTypeEnum newTypeEnum = SmartActionTypeEnum.getByCode(Integer.valueOf(valueNew.toString()));
                DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(I18nStringConstant.PERFORM_ACTION), oldTypeEnum.getMemo(), newTypeEnum.getMemo());
                detailArr.add(detailLogPojo);
            }
        } else if ("executor".equals(key)) {
            if (chage4Array(valueOld, valueNew)) {
                flag = false;
                List<OptionalRangeEntity> oldExecutorList = JsonHelperUtil.parseArray(valueOld.toString(), OptionalRangeEntity.class);
                List<OptionalRangeEntity> newExecutorList = JsonHelperUtil.parseArray(valueOld.toString(), OptionalRangeEntity.class);
                StringBuffer oldNameBuffer = new StringBuffer();
                StringBuffer newNameBuffer = new StringBuffer();
                for (OptionalRangeEntity entity : oldExecutorList) {
                    if (oldNameBuffer.length() == BasicConstant.ZERO) {
                        oldNameBuffer.append(entity.getName());
                    } else {
                        oldNameBuffer.append("、" + entity.getName());
                    }
                }
                for (OptionalRangeEntity entity : newExecutorList) {
                    if (newNameBuffer.length() == BasicConstant.ZERO) {
                        newNameBuffer.append(entity.getName());
                    } else {
                        newNameBuffer.append("、" + entity.getName());
                    }
                }
                DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(I18nStringConstant.EXECUTIVE), oldNameBuffer.toString(), newNameBuffer.toString());
                detailArr.add(detailLogPojo);
            }
        } else if ("smsExecutor".equals(key)) {
            if (chage4Array(valueOld, valueNew)) {
                flag = false;
                List<DataWarningFieldAttrPojo> oldSmsExecutorList = JsonHelperUtil.parseArray(valueOld.toString(), DataWarningFieldAttrPojo.class);
                List<DataWarningFieldAttrPojo> newSmsExecutorList = JsonHelperUtil.parseArray(valueNew.toString(), DataWarningFieldAttrPojo.class);
                StringBuffer oldNameBuffer = new StringBuffer();
                StringBuffer newNameBuffer = new StringBuffer();
                for (DataWarningFieldAttrPojo pojo : oldSmsExecutorList) {
                    if (oldNameBuffer.length() == BasicConstant.ZERO) {
                        oldNameBuffer.append(pojo.getAttrName());
                    } else {
                        oldNameBuffer.append("、" + pojo.getAttrName());
                    }
                }
                for (DataWarningFieldAttrPojo pojo : newSmsExecutorList) {
                    if (newNameBuffer.length() == BasicConstant.ZERO) {
                        newNameBuffer.append(pojo.getAttrName());
                    } else {
                        newNameBuffer.append("、" + pojo.getAttrName());
                    }
                }
                DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(I18nStringConstant.SMS_EXECUTIVE), oldNameBuffer.toString(), newNameBuffer.toString());
                detailArr.add(detailLogPojo);
            }
        } else if ("memo".equals(key)) {
            if (chage4Value(valueOld, valueNew)) {
                flag = false;
                DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(I18nStringConstant.MEMO), valueOld, valueNew);
                detailArr.add(detailLogPojo);
            }
        }
        else if ("color".equals(key)) {
            if (chage4Value(valueOld, valueNew)) {
                flag = false;
                DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(I18nStringConstant.DATA_WARNING_COLOR), valueOld, valueNew);
                detailArr.add(detailLogPojo);
            }
        }else if ("samplingRange".equals(key)) {
            if (chage4Value(valueOld, valueNew)) {
                flag = false;
                DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(I18nStringConstant.DATA_WARNING_SAMPLING_RANGE), valueOld, valueNew);
                detailArr.add(detailLogPojo);
            }
        }else if ("customColors".equals(key)) {
            if (chage4Value(valueOld, valueNew)) {
                flag = false;
                DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(I18nStringConstant.DATA_WARNING_COLOR_CUSTOM), valueOld, valueNew);
                detailArr.add(detailLogPojo);
            }
        }

    }
    if (flag) {
        return null;
    }
    return detailArr;
}

    /**
     * 首页管理日志详情
     *
     * @param corpid 公司id
     * @param oldObj 旧对象
     * @param newObj 新对象
     * @throws
     * @return
     * @author hongxiao
     * @date 2019-12-13 16:21
     * @since v1.0
     * @version v1.0
     */
    public List<DetailLogPojo> deatilLog4HomePageManage(String corpid, JSONObject oldObj, JSONObject newObj) {
        List<DetailLogPojo> detailArr = new ArrayList<>();
        //true 未更改，不需插入详情日志 false 有更改，需插入详情日志
        boolean flag = true;
        for (Map.Entry<String, Object> entry : oldObj.entrySet()) {
            String key = entry.getKey();
            Object valueOld = entry.getValue();
            Object valueNew = newObj.get(key);
            if ("name".equals(key)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(I18nStringConstant.HOME_PAGE_NAME), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("description".equals(key)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(I18nStringConstant.HOME_PAGE_DESCRIPTION), valueOld,valueNew);
                    detailArr.add(detailLogPojo);
                }
            }else if ("range".equals(key)) {
                if (chage4Array(valueOld, valueNew)) {
                    flag = false;
                    List<OptionalRangeEntity> oldRangeList = JsonHelperUtil.parseArray(valueOld.toString(), OptionalRangeEntity.class);
                    List<OptionalRangeEntity> newRangeList = JsonHelperUtil.parseArray(valueOld.toString(), OptionalRangeEntity.class);
                    StringBuffer oldNameBuffer = new StringBuffer();
                    StringBuffer newNameBuffer = new StringBuffer();
                    for (OptionalRangeEntity entity : oldRangeList) {
                        if (oldNameBuffer.length() == BasicConstant.ZERO) {
                            oldNameBuffer.append(entity.getName());
                        } else {
                            oldNameBuffer.append("、" + entity.getName());
                        }
                    }
                    for (OptionalRangeEntity entity : newRangeList) {
                        if (newNameBuffer.length() == BasicConstant.ZERO) {
                            newNameBuffer.append(entity.getName());
                        } else {
                            newNameBuffer.append("、" + entity.getName());
                        }
                    }
                    DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(I18nStringConstant.HOME_PAGE_RANGE), oldNameBuffer.toString(), newNameBuffer.toString());
                    detailArr.add(detailLogPojo);
                }
            }
        }
        if (flag) {
            return null;
        }
        return detailArr;
    }
    /***
     * 决策树操作日志
     *
     * @param: contactNameMap 联系人map
     * @param: logData 日志数据
     * @throws
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogPojo>
     * @author hongxiao
     * @date 2021-12-18 17:34
     * @since
     * @version
     */
    public List<DetailLogComplexPojo> detailLog4DecisionTree(Map<Long, String> contactNameMap, Map<String, String> infoExplainMap, DecisionTreeLogPojo logData) {
        List<DetailLogComplexPojo> detailArr = new ArrayList<>();
        List<DecisionTreeLogDataPojo> addNodes = logData.getAddNodes();
        List<DecisionTreeLogDataPojo> deleteNodes = logData.getDeleteNodes();
        List<DecisionTreeLogDataPojo> updateNodes = logData.getUpdateNodes();

        // 更新日志处理
        handleUpdateNodes(updateNodes, deleteNodes, addNodes);
        detailArr.addAll(getDecisionTreeLogData(contactNameMap, infoExplainMap, addNodes, OperateTypeEnum.NEW));
        detailArr.addAll(getDecisionTreeLogData(contactNameMap, infoExplainMap, deleteNodes, OperateTypeEnum.DELETE));
        detailArr.addAll(getDecisionTreeLogData(contactNameMap, infoExplainMap, updateNodes, OperateTypeEnum.EDIT));

        return detailArr;
    }

    /***
     * 处理决策权树中编辑节点的数据
     *
     * @param updateNodes
     * @param: deleteNodes
     * @param: addNodes
     * @throws
     * @return void
     * @author hongxiao
     * @date 2022-02-10 19:11
     * @since
     * @version
     */
    private void handleUpdateNodes(List<DecisionTreeLogDataPojo> updateNodes, List<DecisionTreeLogDataPojo> deleteNodes, List<DecisionTreeLogDataPojo> addNodes) {
        if (CollectionsUtil.isEmpty(updateNodes)) {
            return;
        }
        // 过虑未修改的节点
        updateNodes.removeIf(logDataPojo -> !checkDecisionTreeNodeUpdate(logDataPojo));
        if (CollectionsUtil.isEmpty(updateNodes)) {
            return;
        }
        if (Objects.isNull(deleteNodes)) {
            deleteNodes = new ArrayList<>();
        }
        if (Objects.isNull(addNodes)) {
            addNodes = new ArrayList<>();
        }
        // 判断是否为先删除联系人再添加联系人
        Iterator<DecisionTreeLogDataPojo> iterator = updateNodes.iterator();
        while (iterator.hasNext()) {
            DecisionTreeLogDataPojo logDataPojo = iterator.next();
            if (!Objects.equals(logDataPojo.getContactId(), logDataPojo.getOldContactId())) {
                // 删除联系人
                DecisionTreeLogDataPojo delDataPojo = new DecisionTreeLogDataPojo();
                delDataPojo.setContactId(logDataPojo.getOldContactId());
                delDataPojo.setParentContactId(logDataPojo.getOldParentContactId());
                deleteNodes.add(delDataPojo);
                // 添加联系人
                DecisionTreeLogDataPojo addDataPojo = new DecisionTreeLogDataPojo();
                addDataPojo.setContactId(logDataPojo.getContactId());
                addDataPojo.setParentContactId(logDataPojo.getParentContactId());
                addDataPojo.setInfo(logDataPojo.getInfo());
                addNodes.add(addDataPojo);
                // 删除更新点节
                iterator.remove();
            }
        }
    }

    /***
     * 获取决策树节点日志信息
     *
     * @param contactNameMap
     * @param: nodes
     * @param: operateTypeEnum
     * @param: detailModuleTypeEnum
     * @throws
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogComplexPojo>
     * @author hongxiao
     * @date 2022-02-10 19:13
     * @since
     * @version
     */
    private List<DetailLogComplexPojo> getDecisionTreeLogData(Map<Long, String> contactNameMap, Map<String, String> infoExplainMap, List<DecisionTreeLogDataPojo> nodes, OperateTypeEnum operateTypeEnum) {
        List<DetailLogComplexPojo> detailArr = new ArrayList<>();
        if (CollectionsUtil.isEmpty(nodes)) {
            return detailArr;
        }

        for (DecisionTreeLogDataPojo dataPojo : nodes) {
            // 外层
            String name = contactNameMap.getOrDefault(dataPojo.getContactId(), "");
            // 内层
            String parentName = contactNameMap.getOrDefault(dataPojo.getParentContactId(), "");
            String oldParentName = contactNameMap.getOrDefault(dataPojo.getOldParentContactId(), "");
            JSONObject info = dataPojo.getInfo();
            JSONObject oldInfo = dataPojo.getOldInfo();
            List<DetailLogPojo> childList = new ArrayList<>();
            // 删除节点不用处理内部数据
            if (OperateTypeEnum.DELETE == operateTypeEnum) {
                DetailLogComplexPojo detailLogPojo = new DetailLogComplexPojo(operateTypeEnum.getName(), name, childList);
                detailArr.add(detailLogPojo);
                continue;
            }
            // 内层数据处理
            if (!Objects.equals(oldParentName, parentName)) {
                DetailLogPojo parentLogPojo = new DetailLogPojo("attr", "所属上级", oldParentName, parentName);
                childList.add(parentLogPojo);
            }

            if (Objects.nonNull(info) || Objects.nonNull(oldInfo)) {
                Set<String> attrList = new HashSet<>();
                if (Objects.nonNull(info)) {
                    attrList.addAll(info.getInnerMap().keySet());
                }
                if (Objects.nonNull(oldInfo)) {
                    attrList.addAll(oldInfo.getInnerMap().keySet());
                }
                for (String attr : attrList) {
                    String infoVlaue = Objects.nonNull(info) && info.containsKey(attr) ? info.getString(attr) : null;
                    String oldInfoValue = Objects.nonNull(oldInfo) && oldInfo.containsKey(attr) ? oldInfo.getString(attr) : null;
                    infoVlaue = StringUtil.isEmpty(infoVlaue) ? "" : infoVlaue;
                    oldInfoValue = StringUtil.isEmpty(oldInfoValue) ? "" : oldInfoValue;
                    if (Objects.equals(infoVlaue, oldInfoValue)) {
                        continue;
                    }
                    DetailLogPojo infoLogPojo = new DetailLogPojo(attr, infoExplainMap.get(attr), oldInfoValue, infoVlaue);
                    childList.add(infoLogPojo);
                }
            }

            DetailLogComplexPojo detailLogPojo = new DetailLogComplexPojo(operateTypeEnum.getName(), name, childList);
            detailArr.add(detailLogPojo);
        }

        return detailArr;
    }

    /***
     * 校验节点是否有更改
     *
     * @param logDataPojo
     * @throws
     * @return boolean
     * @author hongxiao
     * @date 2022-02-10 19:12
     * @since
     * @version
     */
    public boolean checkDecisionTreeNodeUpdate(DecisionTreeLogDataPojo logDataPojo) {
        // 节点当前联系人
        if (!Objects.equals(logDataPojo.getContactId(), logDataPojo.getOldContactId())) {
            return true;
        }
        // 节点上级联系人
        if (!Objects.equals(logDataPojo.getParentContactId(), logDataPojo.getOldParentContactId())) {
            return true;
        }
        // 节点项目决策信息
        JSONObject info = logDataPojo.getInfo();
        JSONObject oldInfo = logDataPojo.getOldInfo();
        // 获取所有key
        Set<String> keys = new HashSet<>();
        if (Objects.nonNull(info)) {
            keys.addAll(info.getInnerMap().keySet());
        }
        if (Objects.nonNull(oldInfo)) {
            keys.addAll(oldInfo.getInnerMap().keySet());
        }
        for (String key : keys) {
            String infoVlaue = Objects.nonNull(info) && info.containsKey(key) ? info.getString(key) : null;
            String oldInfoValue = Objects.nonNull(oldInfo) && oldInfo.containsKey(key) ? oldInfo.getString(key) : null;
            infoVlaue = StringUtil.isEmpty(infoVlaue) ? "" : infoVlaue;
            oldInfoValue = StringUtil.isEmpty(oldInfoValue) ? "" : oldInfoValue;
            if (!Objects.equals(infoVlaue, oldInfoValue)) {
                return true;
            }
        }
        return false;
    }

    /***
     * 合同业绩分配日志
     *
     * @param logList
     * @throws
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogComplexPojo>
     * @author hongxiao
     * @date 2022-05-22 16:20
     * @since
     * @version
     */
    public List<DetailLogComplexPojo> detailLog4ContractPerformance(List<ContractPerformanceLogPojo> logList) {
        if (CollectionsUtil.isEmpty(logList)) {
            return null;
        }
        // 分类：添加、删除、更新
        List<ContractPerformanceLogPojo> addLogList = new ArrayList<>();
        List<ContractPerformanceLogPojo> updateLogList = new ArrayList<>();
        List<ContractPerformanceLogPojo> delLogList = new ArrayList<>();
        for (ContractPerformanceLogPojo logPojo : logList) {
            ContractPerformanceEntity oldEntity = logPojo.getOldEntity();
            ContractPerformanceEntity newEntity = logPojo.getNewEntity();
            if (Objects.isNull(oldEntity) && Objects.nonNull(newEntity)) {
                addLogList.add(logPojo);
            } else if (Objects.nonNull(oldEntity) && Objects.isNull(newEntity)) {
                delLogList.add(logPojo);
            } else {
                updateLogList.add(logPojo);
            }
        }
        List<DetailLogComplexPojo> detailArr = new ArrayList<>();
        detailArr.addAll(getContractPerformanceLogData(addLogList, OperateTypeEnum.NEW));
        detailArr.addAll(getContractPerformanceLogData(delLogList, OperateTypeEnum.DELETE));
        detailArr.addAll(getContractPerformanceLogData(updateLogList, OperateTypeEnum.EDIT));

        return detailArr;
    }

    /***
     * 合同业绩分配日志
     *
     * @param logList
     * @param: operateTypeEnum
     * @throws
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogComplexPojo>
     * @author hongxiao
     * @date 2022-05-22 16:20
     * @since
     * @version
     */
    private List<DetailLogComplexPojo> getContractPerformanceLogData(List<ContractPerformanceLogPojo> logList, OperateTypeEnum operateTypeEnum) {
        List<DetailLogComplexPojo> detailArr = new ArrayList<>();
        if (CollectionsUtil.isEmpty(logList)) {
            return detailArr;
        }
        String businessName;
        for (ContractPerformanceLogPojo logPojo : logList) {
            // 内层
            List<DetailLogPojo> childList = new ArrayList<>();
            ContractPerformanceEntity oldEntity = logPojo.getOldEntity();
            ContractPerformanceEntity newEntity = logPojo.getNewEntity();

            JSONObject oldObj = JsonHelperUtil.parseObject(JSONObject.toJSONString(oldEntity));
            JSONObject newObj = JsonHelperUtil.parseObject(JSONObject.toJSONString(newEntity));

            if (OperateTypeEnum.NEW == operateTypeEnum) {
                setContractPerformanceChildLog(childList, null, newObj);
                businessName = I18nMessageUtil.getMessage(XbbRefTypeEnum.CONTRACT_PERFORMANCE.getName());
            } else if (OperateTypeEnum.DELETE == operateTypeEnum) {
                setContractPerformanceChildLog(childList, oldObj, null);
                businessName = I18nMessageUtil.getMessage(XbbRefTypeEnum.CONTRACT_PERFORMANCE.getName()) + PaasConstant.LEFT_BRACKET  + (oldEntity.getId()) + PaasConstant.RIGHT_BRACKET;
            } else {
                setContractPerformanceChildLog(childList, oldObj, newObj);
                businessName = I18nMessageUtil.getMessage(XbbRefTypeEnum.CONTRACT_PERFORMANCE.getName()) + PaasConstant.LEFT_BRACKET  + (oldEntity.getId()) + PaasConstant.RIGHT_BRACKET;
            }

            // 外层
            DetailLogComplexPojo detailLogPojo = new DetailLogComplexPojo(operateTypeEnum.getName(), businessName, childList);
            detailArr.add(detailLogPojo);
        }


        return detailArr;
    }

    /***
     * 合同业绩分配日志：复杂数据
     *
     * @param childList
     * @param: oldObj
     * @param: newObj
     * @throws
     * @return void
     * @author hongxiao
     * @date 2022-05-22 16:21
     * @since
     * @version
     */
    private void setContractPerformanceChildLog(List<DetailLogPojo> childList, JSONObject oldObj, JSONObject newObj) {
        if (Objects.isNull(oldObj) && Objects.isNull(newObj)) {
            return;
        }
        Set<String> keySet = Objects.nonNull(oldObj) ? oldObj.keySet() : newObj.keySet();
        for (String key : keySet) {
            String valueOld = Objects.nonNull(oldObj) ? oldObj.getString(key) : "";
            String valueNew = Objects.nonNull(newObj) ? newObj.getString(key) : "";
            if ("userName".equals(key)) {
                if (chage4Value(valueOld, valueNew)) {
                    DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(CommonConstant.EMPLOYEE), valueOld, valueNew);
                    childList.add(detailLogPojo);
                }
            } else if ("rate".equals(key)) {
                if (chage4Value(valueOld, valueNew)) {
                    DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(ContractPerformanceEnum.RATE.getAttrName()), valueOld, valueNew);
                    childList.add(detailLogPojo);
                }
            } else if ("del".equals(key)) {
                if (chage4Value(valueOld, valueNew)) {
                    DelEnum oldDelEnum;
                    DelEnum newDelEnum;
                    try {
                        Integer oldDelInt = Integer.parseInt(valueOld);
                        Integer newDelInt = Integer.parseInt(valueNew);
                        oldDelEnum = DelEnum.getByDel(oldDelInt);
                        newDelEnum = DelEnum.getByDel(newDelInt);
                    } catch (NumberFormatException e) {
                        oldDelEnum = null;
                        newDelEnum = null;
                    }
                    if (Objects.nonNull(oldDelEnum) && Objects.nonNull(newDelEnum)) {
                        DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(ContractPerformanceEnum.RATE.getAttrName()), valueOld, valueNew);
                        childList.add(detailLogPojo);
                    }
                }
            }
        }
    }

    /**
     * 图片修改日志
     * date: 2022/6/14 3:10 下午
     * @Author yingjf
     * @param detailArr:
     * @param valueOld:
     * @param valueNew:
     * @param attr:
     * @param attrName:
     */
    private void log4Image(List<DetailLogPojo> detailArr,Object valueOld,Object valueNew,String attr,String attrName){
        try {
            List oldStrList = new ArrayList();
            List<String> updataBeforeList = new ArrayList<>();
            List<String> updataAfterList = new ArrayList<>();
            if(valueOld!=null){
                updataBeforeList = JSONArray.parseArray(JSON.toJSON(valueOld).toString(), String.class);
            }
            if(valueNew!=null){
                updataAfterList = JSONArray.parseArray(JSON.toJSON(valueNew).toString(), String.class);
            }
            if(updataBeforeList!=null){
                updataBeforeList.forEach(item->{
                    oldStrList.add(String.format(SymbolConstant.BRACKETS_SQUARE_SOLID,item ));
                });
            }

            List newStrList = new ArrayList();
            if(updataAfterList!=null){
                updataAfterList.forEach(item->{
                    newStrList.add(String.format(SymbolConstant.BRACKETS_SQUARE_SOLID,item ));
                });
            }
            if(CompareUtil.compareObject(oldStrList, newStrList)){
                return;
            }
            detailArr.add(new DetailLogPojo(attr, attrName, StringUtils.join(oldStrList, ",")
                    , StringUtils.join(newStrList, ",")));
        }catch (Exception e){
            LOG.error("com.xbongbong.saas.service.toolbox.help.LogHelp.log4Image",e);
        }

    }

    /**
     * 附件修改日志
     * date: 2022/6/14 3:10 下午
     * @Author yingjf
     * @param detailArr:
     * @param valueOld:
     * @param valueNew:
     * @param attr:
     * @param attrName:
     */
    private void log4Upload(List<DetailLogPojo> detailArr,Object valueOld,Object valueNew,String attr,String attrName){
        try {
            List<PaasAttachmentEntityExt> updataBeforeEntity = new ArrayList<>();
            List<PaasAttachmentEntityExt> updataAfterEntity = new ArrayList<>();
            if(valueOld!=null){
                updataBeforeEntity = JSONArray.parseArray(JSON.toJSON(valueOld).toString(), PaasAttachmentEntityExt.class);
            }
            if(valueNew!=null){
                updataAfterEntity = JSONArray.parseArray(JSON.toJSON(valueNew).toString(), PaasAttachmentEntityExt.class);
            }
            List<String> updataBeforeStrList = new ArrayList<>();
            if(CollectionsUtil.isNotEmpty(updataBeforeEntity)){
                updataBeforeEntity.forEach(item->{
                    updataBeforeStrList.add(item.getFilename()+String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, item.getAttachIndex()));
                });
            }
            List<String> updataAfterStrList = new ArrayList<>();
            if(CollectionsUtil.isNotEmpty(updataAfterEntity)){
                updataAfterEntity.forEach(item->{
                    updataAfterStrList.add(item.getFilename()+String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, item.getAttachIndex()));
                });
            }
            if(CompareUtil.compareObject(updataBeforeStrList, updataAfterStrList)){
                return;
            }
            detailArr.add(new DetailLogPojo(attr, attrName,
                    StringUtils.join(updataBeforeStrList,",") ,
                    StringUtils.join(updataAfterStrList,",") ));
        }catch (Exception e){
            LOG.error("com.xbongbong.saas.service.toolbox.help.LogHelp.log4Upload",e);
        }

    }


    /**
     * 公司配置字段值改动校验
     *
     * @param corpid 公司id
     * @param oldValue 旧值
     * @param newValue 新值
     * @param companyConfigEnum  公司配置枚举
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogPojo>
     * @author zyy
     * @date 2022/3/19
     * @version v1.0
     */
    public List<DetailLogPojo> detailLog4WorkDay(String corpid, String oldValue, String newValue, CompanyConfigEnum companyConfigEnum) {
        List<DetailLogPojo> detailLogPojoList = new ArrayList<>();
        boolean flag = true;
        switch (companyConfigEnum){
            case WORKING_DAY_MONTH:
                if(chage4Value(oldValue,newValue)){
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(companyConfigEnum.getAlias(),I18nMessageUtil.getMessage(I18nStringConstant.WORKING_DAY_MONTH), oldValue, newValue);
                    detailLogPojoList.add(detailLogPojo);
                }
                break;
            case WORKING_DAY_WEEK:
                if(chage4Value(oldValue,newValue)){
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(companyConfigEnum.getAlias(),I18nMessageUtil.getMessage(I18nStringConstant.WORKING_DAY_WEEK), oldValue, newValue);
                    detailLogPojoList.add(detailLogPojo);
                }
                break;
            case FISCAL_YEAR:
                if(chage4Value(oldValue,newValue)){
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(companyConfigEnum.getAlias(),I18nMessageUtil.getMessage(I18nStringConstant.FISCAL_YEAR), oldValue, newValue);
                    detailLogPojoList.add(detailLogPojo);
                }
            default:
                break;
        }
        if(flag){
            return null;
        }
        return detailLogPojoList;
    }

    /**
     * 编辑目标时，对比字段是否进行了变更
     *
     * @param corpid 公司唯一id
     * @param oldAttr 旧对象
     * @param newAttr 新对象
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogPojo>
     * @author zyy
     * @date 2022/3/21
     * @version v1.0
     */
    public List<DetailLogPojo> detailLog4PerformanceOfEdit(String corpid, JSONObject oldAttr, JSONObject newAttr) throws XbbException {
        List<DetailLogPojo> detailLogPojoList = new ArrayList<>();
        for (Map.Entry<String, Object> oldMap : oldAttr.entrySet()) {
            String key = oldMap.getKey();
            Object oldValue = oldMap.getValue();
            Object newValue = newAttr.get(key);
            if("name".equals(key)){
                //目标名称
                if(chage4Value(oldValue,newValue)){
                    DetailLogPojo detailLogPojo = log4Normal(key, PerformanceLogEnum.NAME.getName(), oldValue, newValue);
                    detailLogPojoList.add(detailLogPojo);
                }
            }else if("memo".equals(key)){
                //目标简介
                if(chage4Value(oldValue,newValue)){
                    DetailLogPojo detailLogPojo = log4Normal(key, PerformanceLogEnum.MEMO.getName(), oldValue, newValue);
                    detailLogPojoList.add(detailLogPojo);
                }
            }else if("driverSources".equals(key)){
                //数据来源
                Map<String, Object> appIdMap = getAttrValueAndTitle(oldValue, newValue, "appId");
                Map<String, Object> formIdMap = getAttrValueAndTitle(oldValue, newValue, "formId");
                String oldAppId = String.valueOf(appIdMap.get("oldValue"));
                String oldFormId = String.valueOf(formIdMap.get("oldValue"));
                String newAppId = String.valueOf(appIdMap.get("newValue"));
                String newFormId = String.valueOf(formIdMap.get("newValue"));
                if(StringUtils.isNotBlank(oldAppId) && StringUtils.isNotBlank(oldFormId) && StringUtils.isNotBlank(newAppId) && StringUtils.isNotBlank(newFormId)){
                    if(chage4Value(oldAppId,newAppId) || chage4Value(oldFormId,newFormId)){
                        //获取一级名称
                        List<PaasAppEntity> appList = paasAppModel.getNameByKeys(Arrays.asList(Long.valueOf(oldAppId),Long.valueOf(newAppId)), corpid);
                        //获取二级名称
                        List<PaasFormEntity> formList = paasFormModel.getNameByKeys(Arrays.asList(Long.valueOf(oldFormId),Long.valueOf(newFormId)),corpid);
                        String oldBuilder = getPaasName4List(appList,Long.valueOf(oldAppId)) + StringConstant.SHORT_BARS + getPaasName4List(formList,Long.valueOf(oldFormId));
                        String newBuilder = getPaasName4List(appList,Long.valueOf(newAppId)) + StringConstant.SHORT_BARS + getPaasName4List(formList,Long.valueOf(newFormId));
                        DetailLogPojo detailLogPojo = log4Normal(key,PerformanceLogEnum.DRIVERSOURCES.getName(), oldBuilder, newBuilder);
                        detailLogPojoList.add(detailLogPojo);
                    }
                }
            }else if("sourceAttr".equals(key) || "belongAttr".equals(key) || "timeAttr".equals(key)){
                //考核指标、归属人、统计日期
                Map<String,Object> map = getAttrValueAndTitle(oldValue,newValue,"attrName");
                //考核名称
                if(chage4Value(map.get("oldValue"),map.get("newValue"))){
                    DetailLogPojo detailLogPojo = log4Normal(key,PerformanceLogEnum.getNameByKey(key),map.get("oldValue"),map.get("newValue"));
                    detailLogPojoList.add(detailLogPojo);
                }
            }else if("permissions".equals(key)){
                //考核对象
                ChartPermissionsPojo oldPojo = JsonHelperUtil.parseObject(JSONObject.toJSONString(oldValue), ChartPermissionsPojo.class);
                ChartPermissionsPojo newPojo = JsonHelperUtil.parseObject(JSONObject.toJSONString(newValue), ChartPermissionsPojo.class);
                if(chage4Value(oldPojo.toString(),newPojo.toString())){
                    //变更前的考核对象数组
                    ChartPermissionDetailsPojo oldVisibleScopeRule = oldPojo.getVisibleScopeRule();
                    String oldVar = "";
                    if(oldVisibleScopeRule != null && CollectionsUtil.isNotEmpty(oldVisibleScopeRule.getDep())){
                        List<String> oldList = oldVisibleScopeRule.getDep().stream().map(DepartmentEntity::getName).collect(Collectors.toList());
                        oldVar = replaceBrackets(oldList);
                    }
                    //变更后的考核对象数组
                    ChartPermissionDetailsPojo newVisibleScopeRule = newPojo.getVisibleScopeRule();
                    String newVar = "";
                    if(newVisibleScopeRule != null && CollectionsUtil.isNotEmpty(newVisibleScopeRule.getDep())){
                        List<String> newList = newVisibleScopeRule.getDep().stream().map(DepartmentEntity::getName).collect(Collectors.toList());
                        newVar = replaceBrackets(newList);
                    }
                    DetailLogPojo detailLogPojo = log4Normal(key,PerformanceLogEnum.PERMISSIONS.getName(),oldVar,newVar);
                    detailLogPojoList.add(detailLogPojo);
                }
            }else if("explains".equals(key)){
                //最小考核周期
                PerformanceSetExplainPojo oldPojo = JsonHelperUtil.parseObject(JSONObject.toJSONString(oldValue), PerformanceSetExplainPojo.class);
                PerformanceSetExplainPojo newPojo = JsonHelperUtil.parseObject(JSONObject.toJSONString(newValue), PerformanceSetExplainPojo.class);
                if(chage4Value(oldPojo.getMinCycle(),newPojo.getMinCycle())){
                    String oldVar = MinCycleEnum.getByCodeNotNull(oldPojo.getMinCycle());
                    String newVar = MinCycleEnum.getByCodeNotNull(newPojo.getMinCycle());
                    DetailLogPojo detailLogPojo = log4Normal(key,PerformanceLogEnum.MINCYCLE.getName(),oldVar,newVar);
                    detailLogPojoList.add(detailLogPojo);
                }
                //考核方式
                if(chage4Value(oldPojo.getAssessType(),newPojo.getAssessType())){
                    String oldVar = AssessTypeEnum.getByCodeNotNull(oldPojo.getAssessType());
                    String newVar = AssessTypeEnum.getByCodeNotNull(newPojo.getAssessType());
                    DetailLogPojo detailLogPojo = log4Normal(key,PerformanceLogEnum.ASSESSTYPE.getName(),oldVar,newVar);
                    detailLogPojoList.add(detailLogPojo);
                }
            }else if("statisticsWay".equals(key)){
                //计算方式
                AggTypeEnum oldEnum = AggTypeEnum.getByCode(Integer.parseInt(oldValue.toString()));
                AggTypeEnum newEnum = AggTypeEnum.getByCode(Integer.parseInt(newValue.toString()));
                String oldEnumName = oldEnum.getName();
                String newEnumName = newEnum.getName();
                if(chage4Value(oldEnumName,newEnumName)){
                    DetailLogPojo detailLogPojo = log4Normal(key,PerformanceLogEnum.STATISTICSWAY.getName(),oldEnumName,newEnumName);
                    detailLogPojoList.add(detailLogPojo);
                }
            }else if("unit".equals(key)){
                //单位
                //系统、自定义图表该字段传值统一为 numbers、money、custom等
                String oldEnumName = UnitEnum.getByAliasNotNull(String.valueOf(oldValue));
                String newEnumName = UnitEnum.getByAliasNotNull(String.valueOf(newValue));
                if(chage4Value(oldEnumName,newEnumName)){
                    DetailLogPojo detailLogPojo = log4Normal(key,PerformanceLogEnum.UNIT.getName(),oldEnumName,newEnumName);
                    detailLogPojoList.add(detailLogPojo);
                }
            }else if("penetrateAttr".equals(key)){
                //穿透表头字段
                if(chage4Array(oldValue,newValue)){
                    JSONArray oldArray = JsonHelperUtil.parseArray(oldValue.toString());
                    JSONArray newArray = JsonHelperUtil.parseArray(newValue.toString());
                    //获取变更前的穿透数组中的name
                    List<Object> oldList = oldArray.stream().map(var -> JSONObject.parseObject(JSONObject.toJSONString(var)).get("attrName")).collect(Collectors.toList());
                    //获取变更后的穿透数组中的name
                    List<Object> newList = newArray.stream().map(var -> JSONObject.parseObject(JSONObject.toJSONString(var)).get("attrName")).collect(Collectors.toList());
                    String oldVar = replaceBrackets(oldList);
                    String newVar = replaceBrackets(newList);
                    DetailLogPojo detailLogPojo = log4Normal(key,PerformanceLogEnum.PENETRATEATTR.getName(),oldVar,newVar);
                    detailLogPojoList.add(detailLogPojo);
                }
            }
        }
        if(detailLogPojoList.size() <= 0){
            return null;
        }
        return detailLogPojoList;
    }
    /**
     * 数据集日志
     * @param corpid
     * @param oldInfo
     * @param newInfo
     * @return java.util.List<com.xbongbong.pro.log.pojo.DetailLogPojo>
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public List<DetailLogPojo> detailLog4DataSetRename(String corpid, DataSetLogInfoPojo oldInfo, DataSetLogInfoPojo newInfo) throws XbbException {
        List<DetailLogPojo> detailLogPojoList = new ArrayList<>();
        Field[] fields = oldInfo.getClass().getDeclaredFields();
        try {
            for(Field field:fields){
                field.setAccessible(true);
                //字段名
                String fieldName = field.getName();
                if (Objects.equals(fieldName, "serialVersionUID")) {
                    continue;
                }
                String oldValue = String.valueOf(field.get(oldInfo));
                String newValue = String.valueOf(ReflectHelper.valueGet(newInfo, fieldName));
                if(chage4Value(oldValue,newValue)){
                    //字段对应的中文名
                    String title = DataSetTitleEnum.getNameByKey(fieldName);
                    DetailLogPojo detailLogPojo = log4Normal(fieldName, title, oldValue, newValue);
                    detailLogPojoList.add(detailLogPojo);
                }
            }
        } catch (IllegalAccessException e) {
            return null;
        }
        if(detailLogPojoList.size() <= 0){
            return null;
        }
        return detailLogPojoList;
    }

    /**
     * 将数组格式为字符串，并去除左右括号
     *
     * @param list  源数组
     * @return java.lang.String  格式化后的字符串
     * @author zyy
     * @date 2022/4/19
     * @version v1.0
     */
    private <T> String replaceBrackets(List<T> list) {
        if(CollectionsUtil.isEmpty(list)){
            return "";
        }
        return list.toString().replace("[","").replace("]","");
    }

    /**
     * 获取 应用名/表单名
     *
     * @param paasList 实体数组
     * @param id  id
     * @return java.lang.String  名称
     * @author zyy
     * @date 2022/4/1
     * @version v1.0
     */
    private String getPaasName4List(List<? extends Entity> paasList, Long id) {
        String name = "";
        if(CollectionsUtil.isEmpty(paasList)){
            return name;
        }
        for (Entity entity : paasList) {
            if (entity instanceof PaasAppEntity) {
                PaasAppEntity appEntity = (PaasAppEntity) entity;
                if (appEntity.getId().equals(id)) {
                    name = appEntity.getName();
                    break;
                }
            } else if (entity instanceof PaasFormEntity) {
                PaasFormEntity paasFormEntity = (PaasFormEntity) entity;
                if (paasFormEntity.getId().equals(id)) {
                    name = paasFormEntity.getName();
                    break;
                }
            }
        }
        return name;
    }

    /**
     * 处理嵌套层级的json字段值
     *
     * @param oldValue  旧对象
     * @param newValue  新对象
     * @param attrName  要获取的字段名
     * @return java.util.Map<java.lang.String,java.lang.Object>
     * @author zyy
     * @date 2022/3/21
     * @version v1.0
     */
    private Map<String, Object> getAttrValueAndTitle(Object oldValue, Object newValue, String attrName) {
        JSONObject oldObj;
        JSONObject newObj;
        try{
            oldObj = JsonHelperUtil.parseObject(JSONObject.toJSONString(oldValue));
            newObj = JsonHelperUtil.parseObject(JSONObject.toJSONString(newValue));
        } catch(Exception e){
            oldObj = new JSONObject();
            newObj = new JSONObject();
        }
        Map<String,Object> result = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        //旧值，key固定为"oldValue"
        result.put("oldValue",oldObj.get(attrName));
        //新值，key固定为"newValue"
        result.put("newValue",newObj.get(attrName));
        return result;
    }

    /**
     * 知识库编辑日志详情
     *
     * @param corpid 公司id
     * @param oldObj 旧对象
     * @param newObj 新对象
     * @throws
     * @return
     * @author hongxiao
     * @date 2019-12-13 16:21
     * @since v1.0
     * @version v1.0
     */
    public List<DetailLogPojo> detailLog4KnowledgeData(String corpid, JSONObject oldObj, JSONObject newObj) {
        List<DetailLogPojo> detailArr = new ArrayList<>();
        boolean flag = true;
        for (Map.Entry<String, Object> entry : oldObj.entrySet()) {
            String key = entry.getKey();
            Object valueOld = entry.getValue();
            Object valueNew = newObj.get(key);
            if ("title".equals(key)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(KnowledgeDataEnum.TITLE.getAttrName()), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("content".equals(key)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(KnowledgeDataEnum.CONTENT.getAttrName()), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if (KnowledgeDataEnum.EXPAND_CONSULT.getAttr().equals(key)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(KnowledgeDataEnum.EXPAND_CONSULT.getAttrName()), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if (KnowledgeDataEnum.KEYWORDS.getAttr().equals(key)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(KnowledgeDataEnum.KEYWORDS.getAttrName()), valueOld, valueNew);
                    detailArr.add(detailLogPojo);
                }
            } else if ("catalogueId".equals(key)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    KnowledgeCatalogueEntity knowledgeCatalogueOld=knowledgeCatalogueModel.getByKey(Long.valueOf(valueOld.toString()),corpid);
                    KnowledgeCatalogueEntity knowledgeCatalogueNew=knowledgeCatalogueModel.getByKey(Long.valueOf(valueNew.toString()),corpid);
                    DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(KnowledgeDataEnum.CATALOGUE.getAttrName()), knowledgeCatalogueOld.getName(), knowledgeCatalogueNew.getName());
                    detailArr.add(detailLogPojo);
                }
            } else if (KnowledgeDataBaseEnum.LABEL.getAttr().equals(key)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    StringJoiner tagStringOld = new StringJoiner(",");
                    if (Objects.nonNull(valueOld) && StringUtils.isNotEmpty(String.valueOf(valueOld))) {
                        List<Long> list = JSONArray.parseArray(String.valueOf(valueOld), Long.class);
                        if (!list.isEmpty()){
                            List<KnowledgeTagEntity> knowledgeTagEntities = knowledgeTagModel.getByTagIdList(list, corpid);
                            for (KnowledgeTagEntity knowledgeTagEntity : knowledgeTagEntities) {
                                tagStringOld.add(knowledgeTagEntity.getName());
                            }
                        }
                    }
                    StringJoiner tagStringNew = new StringJoiner(",");
                    if (Objects.nonNull(valueNew) && StringUtils.isNotEmpty(String.valueOf(valueNew))) {
                        List<Long> list = JSONArray.parseArray(String.valueOf(valueNew), Long.class);
                        if (CollectionsUtil.isNotEmpty(list)){
                            List<KnowledgeTagEntity> knowledgeTagEntities = knowledgeTagModel.getByTagIdList(list, corpid);
                            for (KnowledgeTagEntity knowledgeTagEntity : knowledgeTagEntities) {
                                tagStringNew.add(knowledgeTagEntity.getName());
                            }
                        }
                    }
                    DetailLogPojo detailLogPojo = log4Normal(key, I18nMessageUtil.getMessage(KnowledgeDataEnum.LABEL.getAttrName()), tagStringOld.toString(), tagStringNew.toString());
                    detailArr.add(detailLogPojo);
                }
            } else if ("updateTime".equals(key)) {
                if (chage4Value(valueOld, valueNew)) {
                    flag = false;
                    detailArr = log4UpdateTime(detailArr, "updateTime", valueOld, valueNew);
                }
            }

        }
        if (flag) {
            return null;
        }
        return detailArr;
    }

    /**
     * 竞争对手分析编辑日志详情
     * @param oldData
     * @param newData
     * @return
     */
    public List<DetailLogPojo> detailLog4CompetitorAnalysis(JSONObject oldData,JSONObject newData,PaasFormExplainEntity paasFormExplainEntity){
        //是否有数据修改标志位
        boolean flag = true;

        List<DetailLogPojo> detailLogPojoList = new ArrayList<>();

        log4UpdateTimeDuplicate(detailLogPojoList, CommonConstant.UPDATE_TIME, oldData.get("updateTime"), newData.get("updateTime"));
        oldData.remove("updateTime");
        newData.remove("updateTime");

        for (Map.Entry<String, Object> entry : oldData.entrySet()) {
            String attr = entry.getKey();
            Object oldValue = entry.getValue();
            Object newValue = newData.get(attr);
            if (chage4Value(oldValue,newValue)) {
                flag = false;
                DetailLogPojo detailLogPojo = log4CompetitorAnalysis(attr, oldValue, newValue,paasFormExplainEntity);
                detailLogPojoList.add(detailLogPojo);

            }
        }

        // 数据没有变动，无需插入日志
        if (flag){
            return null;
        }

        return detailLogPojoList;
    }

    /**
     * 获取日志详情
     * @param attr
     * @param oldValue
     * @param newValue
     * @param paasFormExplainEntity
     * @return
     */
    private DetailLogPojo log4CompetitorAnalysis(String attr,Object oldValue, Object newValue,PaasFormExplainEntity paasFormExplainEntity){

        String updateBefore = "";
        String updateAfter = "";
        String attrName = "";

        List<FieldAttrEntity> explainList = JSON.parseArray(paasFormExplainEntity.getExplains(), FieldAttrEntity.class);
        FieldAttrEntity attrEntity = explainList.stream().filter(item -> Objects.equals(attr, item.getAttr())).findFirst().orElse(null);
        if (Objects.nonNull(attrEntity)){
            attrName = attrEntity.getAttrName();
            if (Objects.equals(attrEntity.getFieldType(),FieldTypeEnum.STAR.getType())){
                String oldText = object2String(oldValue);
                String newText = object2String(newValue);
                updateBefore = oldText == null ? "" : oldText + "星";
                updateAfter  = newText == null ? "" : newText + "星";
            }

            // 单行文本类型
            if (Objects.equals(attrEntity.getFieldType(),FieldTypeEnum.TEXT.getType())){
                updateBefore = object2String(oldValue);
                updateAfter = object2String(newValue);
            }

            // 下拉框
            if (Objects.equals(attrEntity.getFieldType(),FieldTypeEnum.COMBO.getType())){
                List<ItemPoJo> items = attrEntity.getItems();
                ItemPoJo oldItemValue = items.stream().filter(item -> Objects.equals(item.getValue(), oldValue)).findFirst().orElse(null);
                updateBefore = oldItemValue != null ? oldItemValue.getText() : "";
                ItemPoJo newItemValue = items.stream().filter(item -> Objects.equals(item.getValue(), newValue)).findFirst().orElse(null);
                updateAfter = newItemValue != null ? newItemValue.getText() : "";
            }
        }

        return new DetailLogPojo(attr,attrName,updateBefore,updateAfter);
    }

    /**
     * 竞争对手swot分析日志详情
     * @param oldSwot
     * @param newSwot
     * @return
     */
    public List<DetailLogPojo> detailLog4CompetitorSwotAnalysis(JSONArray oldSwot,JSONArray newSwot,PaasFormExplainEntity paasFormExplainEntity){

        boolean flag = true;
        List<DetailLogPojo> detailLogPojoList = new ArrayList<>();
        for (int i = 0; i < 4; i++) {
            Object oldQudrant = null;
            // 第一次编辑swot时，为避免oldSwot取数越界
            if (oldSwot.size() != 0){
                oldQudrant = oldSwot.get(i);
            }
            Object newQudrant = newSwot.get(i);
            if (change4SwotQuadrant(oldQudrant,newQudrant)){
                flag = false;
                DetailLogPojo detailLogPojo = log4SwotAnalysis(i, oldQudrant, newQudrant,paasFormExplainEntity);
                detailLogPojoList.add(detailLogPojo);
            }
        }

        if (flag){
            return null;
        }

        return detailLogPojoList;
    }


    /**
     * 比较swot分析中的象限元素是否相同
     * @param oldQudrant
     * @param newQudrant
     * @return
     */
    private boolean change4SwotQuadrant(Object oldQudrant, Object newQudrant){

        Object[] oldEleList = object2List(oldQudrant).toArray();
        Object[] newEleList = object2List(newQudrant).toArray();

        return !Arrays.equals(oldEleList,newEleList);
    }


    private DetailLogPojo log4SwotAnalysis(Integer index ,Object oldValue,Object newValue,PaasFormExplainEntity paasFormExplainEntity){

        List<String> updateBefore = object2List(oldValue);
        List<String> updateAfter = object2List(newValue);
        SwotIndexEnum swotIndexEnum = SwotIndexEnum.getByIndex(index);
        if (swotIndexEnum == null){
            throw new NullPointerException("swotIndexEnum is null");
        }

        List<FieldAttrEntity> attrEntityList = JSON.parseArray(paasFormExplainEntity.getExplains(), FieldAttrEntity.class);
        String before = attrEntityList.stream().filter(item -> updateBefore.contains(item.getAttr())).map(item -> item.getAttrName()).collect(Collectors.joining(","));
        String after = attrEntityList.stream().filter(item -> updateAfter.contains(item.getAttr())).map(item -> item.getAttrName()).collect(Collectors.joining(","));
        return new DetailLogPojo(swotIndexEnum.getName(),before,after);
    }



}
